//[of]:(testing)
function debugMessage(message)				//MAKE EMPTY STRING FOR FINAL GAME!!!
{	document.getElementById("SPdebugMessage").innerHTML =
		"<div style=\"position: absolute; top:20px; left: 740px; z-index: 3000000\"><h4>Debugging:<br>"+message+"</h4></div>";
}
//[c]

var lorem ="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
function runTestCode(string)
{	stopEverything();

	say("","defaultFont",1);
	say("","bigFont",2);
	say("","handFont",3);

//message("running test code on person/object click");

/*
	say("","First I will turn round.");
	turn("l");
//	ww[sn].friend ="cfLover";
//	say("","Hello friend. Bla bla bla bla bla bla bla bla bla.$Hello.");
	sit();
	say("","I am talking while sitting down.");
	stand();
	say("","Click the floor to make me walk there.");
*/
	endClick();
	return 1;	// so parent function can choose not to go any further
}

//[cf]
//[of]:general
//[of]:(reference)
//[c]GOAL: 	online library of all famous books
//[c]		QUICK GAMES - grab the curious + visionary
//[c]		workshop -like heritage blacksmith, woodcarver, glassblower, etc.

	
//[c]KNOWN ISSUES:
//[c]		Android fonts: will not change font, except to Google
//[c]		Opera: does not remove images properly - moving walkers leave trails (the part that is not covered by the new frame)
//[c]				Google fonts briefly flash as default font first
	
			
//[c]Z VALUES:
//[c]	  0 -	998			= paralax etc
//[c]              	999 		= background
//[c]   1010 -	960999		= 960 pixels		1010 -  	1990	 = person 1-99 (10 'z's each)= n*10 +1000
//[c]										+4=body; +5=face; +1-3=halo, backpack; +6-9=arm,box,glasses, etc
//[c]										2010 -  	2999 	 = same for next layer of pixels (so people on y=2 are always in front of people on y=1)
//[c]		 								960010 - 960999 = same for y =960 (=480 + 480 high object below screen, but head is still visible)
//[c] 970000 -	979999 		= foreground	(trees, etc.)
//[c] 980000 -	980999 		= overlays 		(full screen tints etc)
//[c]			981000	 	= book			(index.html)
//[c] 			982000 		= talk
//[c]			1000000	= window		(cannot click through transparency, so all objects must be behind this)
//[c]1000001 -	1989999 	= avatars		(counterparts to main bodies. NOTE: smartphones need very simple shapes)
//[c]			2000000	= fading		(fade to black, etc: do not want clickable at this point)
//[c]2900000 -	3000018 	= menu			2900000				= menu back
//[c]										2900001 -	2999999 	= menu items
//[c]										3000000 -	3000009	= message box			(shadow, image, text, clickable full screen)
//[c]										3000011-	3000018 	= bookmarks			( 8 menu tems)
//[c]			3000100	= mouseover			(potentially multiple layers one day)
//[c]			3000011+ 	= reserved:		3000011+ -	3000099 	= (if characters ever climb out of the window: on top of everything else)
//[c]										10000000 -	13000199 	= (2nd game: add 10 million to make second game run on top)
//[c]		 								2147483647 	= maximum z value: 32bit signed integer.
	
	
//[c]GUIs.js
//[c]main features: ---------------------------------------------------------------
//[c]	
//[c]	
//[c]	Big div list:	Bookmarks (load/save), recent anything, etc. Note smartphone size: just ONE list per page.
//[c]					Plotpoints= contents (chapter numbers) with optional 'toggle spoilers' button (for names + triggers)
//[c]					Arrows: each div has the existing arrows added. Remember to re-position divs for next screen!
//[c]					Double as buttons and labels, with optional images: text speed, next page, etc.
//[c]					Text speed, 
//[c]	Text box:		Anything from intro help to letter to complete book. Read text file. Look for handwriting font if available.
//[c]					Recent dialog: have slect all buttona nd message pop-up for saving, note javascript security limitations.	
//[c]	trivially simple features: ---------------------------------------------------
//[c]	Text box 2:		Same code as twesb box 1. Autoscroll credits: vCan javascript choose which line is at the top?
//[c]	Help button:	Big button at right. Loads most recent GUI plus menu bar at top.	
//[c]	Imagemap:		For map.
//[c]	Highlighter: 	image highlights current div object
//[c]	Messagebox:		Stretchable image - no need for corners
//[c]	FadeImage:		Fading between games, fade to black, flashback image, etc.
//[c]	testversion:	Test version, etc.	


//[c]MEMORY USAGE
//[c]	(in Chrome)
//[c]	19k per wardrobe x 4 per person (76k) x 83 people =6 mb total. 
//[c]	Could reduce wardrobes to 2MB by using arrays, but makes code less convenient & adds search delay to every loop.
//[c]	Whole page =48MB.
//[c]	Compared to 19 MB (very simple Zak-site page), 79 MB (Hotmail - uses two processes), or 107 MB (YouTube)

//[cf]
//[of]:simple text
function simpleText(text,link,font,color,fontSizeAdd,width,height,x,y,z) // in table for vertical positioning
{	// centered
//[c] variables
	var shadow 		= arguments[9]; if((shadow ==null)||(shadow =="")) shadow =shadowColor;
	if(shadowColor = "")				shadowColor = "White";	
				// font shadows are rendered very pale, so needs white even for grey rooms.
	if((font ==null) ||(font ==0)) 		font		= 1;
	if((color ==null)||(color =="")) 	color 		= textColor[0];
	if(fontSizeAdd ==null)		 		fontSizeAdd	= 0;
	if(font ==2) fontSizeAdd = fontSizeAdd +80;			// large font
	if(font ==3) fontSizeAdd = fontSizeAdd +30;			// handwriting font is naturally smaller
	if((width ==null)||(width ==0))		width 		= 3 *fontSize; 
	if((height ==null)||(height ==0))	height 		= 50; 
	var fontSize 	= defaultFontSize + fontSizeAdd;
//[c]  html strings
	var divT 		= "<div style=\"position: absolute; top:" +y +"px; left: " +x 
						+"px; width:" +width +"px; height:" +height +"px; "
						+"text-shadow: " +shadow +" 0em 0em 0.4em; z-index: " +z +"\">";
	var tableT 		= "<table style=\"width:" +width +"px; height:" +height +"px;\" "
						+"border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
						+"<tbody><tr><td style=\"vertical-align: center;\">";
	var textLink 	= "";
	if(link !="")textLink =	"onClick=\"" +link +"\" onMouseDown=\"" +link +"\" onMouseUp=\"" +link +"\"";
	var fontT 		= "<h" +font +" style=\"font-size:" +fontSize +"%; color:'" +color +"'\" " +textLink +">";
	var endT 		= "</h" +font +"></td></tr></tbody></table></div>";
	var bigString 	= divT +tableT +fontT +text +endT;
	return bigString;
}

//[cf]
//[of]:array tools
//[c]  the size of an associative array		 e.g. myArray["stringName"] = "stringValue"
//[c]   									(though technically javascript does not have associative arrays, only objects)
//[c]  	by James Coglan, Stackoverflow: "There's a sort of convention in JavaScript that you don't add things to Object.prototype, because 
//[c]   									 it can break enumerations in various libraries. Adding methods to Object is usually safe, though."
Object.size = function(obj) 
{   var size = 0, key;
    for (key in obj) 
    {   if (obj.hasOwnProperty(key)) size++;
    }
    return size;
};
//[c]  cloning an object					from http://www.hardcode.nl/subcategory_1/article_414-copy-or-clone-javascript-array-object
function cloneObject(source) 
{   for (i in source) 
	{	if (typeof source[i] == 'source') 
				this[i] = new cloneObject(source[i]);
        else	this[i] = source[i];
	}
}
//[c]
//[cf]


function allert(what)// spelling: so you don't waste time with these when searching for test a-lerts
{	alert(what);
}
function checkJavascript(message)			// mainly for screen loading message: so user can tell if it has stalled
{	document.getElementById("SPloadingMessage").innerHTML ="<div><h4>"+message+"</h4></div>";
}
//[cf]
//[of]:variables
//[c]  IE may want these at the top
var block 		= 0;			// 0 =no block, 1=cutscene, 3=frozen for message on screen
var GUIon	= 0;
var allowClick 	= 1;
var talker 		= "";			// so you don't remove dialog if 'stopTalking' called for somebody else
var tempXY		= [0,0];		// for any function that must immediately return 2 variables
//[c]  fonts: h1 =normal, h2 =bold italic, h3 = handwriting, h4 =testing, h5 = on background, h6 = mouseover
var defaultFontSize = 140;
//[c]  set by user
var readingSpeed = 75;			// characters per second, plus 2 seconds' padding. User can adjust.
//[c]  room
var thisRoom 	= 0;			
var lastRoom 	= 0;
var floorEdges 	= [0,240,640,480];// [x1,y1,  x2,y2]
var floorScale 	= [100,100];	// [top of floor, bottom of floor]
var roomColor 	= "White";		// only "White" or "DimGrey" allowed (#696969) - needs separate set of sprites.
								// "dimGrey" actually looks fine against both foggy AND near-black rooms.  
//[c]  overrides
// talkturn, music, etc.
//[c]  easter egg changes star name
var userName1	= "username1";
var userName2	= "username2";


//[of]:only set at start
//[c]  change when selling
var blankPic 		= "images/icons_etc/plain/clear_person_test.gif"; // change this before selling!
//[c]  change for each game
var starId			= "cmHero";			// just a string: w("sn") will return storyStar object
//[c]  maybe change for each game
var darkTextCol 	= ["MidnightBlue","DarkRed"];// MidnightBlue= "#191970"; DarkRed ="#8B0000":
var lightTextCol 	= ["White","Yellow"];
//[c]  main arrays (leave these alone)
var ww 				= new Array(); 		// ww = who/what = people and other objects
var rooms			= new Array();		
var chapters		= new Array();
var sponTalk		= new Array();		// spontaneous talk
var saveDialog		= new Array();		// saves last 100 lines of dialog
//[c]  other (leave these alone)
var chapTotal		= 0;				// automatically updated as chapters are made
var starNum			= 0;		
var sn 				= 0;				// short version of starName for ww[sn]
var IE 				= document.all?true:false;
var framesPerSec 	= 30;
var timePerFrame 	= 1000/framesPerSec; 	// 1000ths of a second
var downloadQueue 	= ["","","","",""];	// last few attempts to download images
var downloadQTime 	= [0,0,0,0,0,0];	// how long you've been trying to download
var windowZ =999999;					// for deciding if an item needs click code
	
//[cf]
//[of]:automatically change
var timeQueue	= new Array();
var gameTime	= 0;				
var mouseX 		= 320;			// mouse positions, updated constantly
var mouseY		= 240; 	
var oldMouse 	= [0,0];
var mouseStill 	= 0;			// if mouse is still for too long, hide mouseover
var loop30 		= 0;			// loop30 counts up from zero. hits 30: makes any infrequent checks.
var textColor	= ["",""];
	textColor[0] = darkTextCol[0];
	textColor[1] = darkTextCol[1];
var sponTimer	= 0;			// for spontaneous talk
var chapterToEnd = "";
//[cf]






//[c]
//[of]:MATH etc.
//[of]:overlaps
function lineOverlaps( line1start, line1stop, line2start, line2stop)
{	// don't assume that x1 < x2 -------------------------------------------------
	var line1SmallestX 	= line1start;	
	var line1BiggestX 	= line1stop;
	var line2SmallestX 	= line2start;
	var line2BiggestX 	= line2stop;
	if(line1SmallestX 	> line1BiggestX)
	{	line1SmallestX 	= line1stop; 
		line1BiggestX 	= line1start;
	}
	if(line2SmallestX 	> line2BiggestX)
	{	line2SmallestX 	= line2stop; 
		line2BiggestX 	= line2start;
	}
	// do they overlap?
	if(line1SmallestX 	> line2BiggestX) 	return 0; 		// totally on one side?
  	if(line1BiggestX 	< line2SmallestX)	return 0; 		// totally on the other?
	return 1;												// still here? Then some part must overlap
}

function areaOverlaps( area1x1, area1x2, area1y1, area1y2, area2x1, area2x2, area2y1, area2y2)
{	if(!lineOverlaps(area1x1,area1x2, area2x1,area2x2)) return 0;
	return lineOverlaps(area1y1,area1y2, area2y1,area2y2); 	// still here? (see note)
//[c]  still here? then if 'Y' overlaps as well, some part must overlap
}

//[cf]
//[of]:text
function replaceAll(txt, replace, with_this) 
{	return txt.replace(new RegExp(replace, 'g'),with_this);
}
//[cf]

function scaleIt(toScale,scale)
{	if(scale ==1.0)return toScale;
	var result = Math.round(toScale * scale);
	if(result ==0)
	{	if(toScale >0)return 1; 
		return -1;
	} return result;
}

function findDist( x, y, x2, y2)
{	x = x - x2;		// the difference
	y = y - y2;		// 
	var squared 	= (x * x) + (y * y);
	return Math.Sqrt(squared);
}
function randomPointAlongLine( x1, y1, x2, y2) // fills GlobalInt 17-18
{	var howFar		= Math.random();		// returns 0-0.999...10
	var Xdif	 	= howFar *(x2 - x1);
	var Ydif	 	= howFar *(y2 - y1);
	tempXY			= [x1 + Xdif, y1 + Ydif,];
}

//[cf]
//[c]



//[cf]
//[of]:objects
//[c] basics 
function mO() 						// mO = myObject
{
//[c]  position etc
	this.room 			= -1;			
	this.name 			= ""; 		// id = cmHero, etc - fixed between games for code re-use.
	this.feet 			= [0,0];	// position of feet on screen (picX,picY =top left of image)
	this.scaleNow 		= 1.0;  			
	this.scale 			= oScale;
	this.autoscale 		= 1;
//[c]  characteristics
	this.friend			= "";
	this.restricted		= 0;		// 1= sitting: YES talk, NO move
//[c]  functions
  	this.getLoop 		= oGetLoop;		
	this.updateBothPics = oUpdateBothPics;
//[c]  actions order of precedence: talk, turn, walk, idle
	this.turn 			= oTurn;
	this.turnXY 		= oTurnXY;
	this.walk 			= oWalk;				
	this.adjustSpeed 	= oAdjustSpeed;
	this.wait 			= oWait;	
	this.stopWalk		= oStopWalk;
//[c]  talk to: uses 'speechBlock' object
	this.talkto			= new Array();	// 'speechBlock' objects: last element is never removed
	this.talkOtherRoom  = "";			// seldom used, but in case triggered from 'recent'
//[c]  wardrobes
	this.wardrbs		= new Array();	// wardrobes["c19"] =myWardrobe; document.write(wardrobes["c19"].y); 	
	this.wardrbs["myWard"] = new wardrobe();	// everyone has one. other wardrobes are only added as needed.

//[of]:internal vars etc
//[c]											FUNCTIONS ONLY USED BY CODE
	this.checkCostume 	= oCheckCostume;		
//[c]											VARIABLES ONLY CHANGED BY CODE
//[c]  set at the start
	this.who 		= 0;		// used in ww[who], counts up at the start, never changed	
	this.id 		= "codename"; // the same between games (the visible name changes of course)
	this.howManyDirections =4;	// strangers, cfAngel and cmHero do 8, others do 4 or 0
	this.personType	= 1;		// 0 = object (does not talk)
//[c]  copied from loop
	this.feetOffset = [0,0];		
	this.size 		= [0,0];	// size of unscaled graphic	
	this.faceSize 	= [0,0];
//[c]  calculated from scale
	this.facePicSize =[0,0];	// scaleIt size
	this.picXYZ 	= [0,0,0]; 	// Z depends on y poisition etc - do not change directly
	this.picSize 	= [0,0];	// where plotted
	this.picCXYZ 	= [0,0,0];	
	this.picCSize 	= [0,0];	// C =clickable image 
//[c]  used by 'checkCostume'
	this.animating 	= 1; 		// worth changing HTML? (default =1: turned off if that loop only has 1 frame)
	this.img 		= "";
	this.mouthImg 	= "";
//[c]  used by 'turn'
	this.dir	 	= "f"; 		
	this.targetXYDir = "";		// f=front targetDir = keep turning one step at a time until there
//[c]  used by 'walk'
	this.targetXY 	= [0,0]; 	
	this.nextStep 	= [0,0];	// used by checkCostumes
	this.speed 		= 14; 	  	// canged by 'setspeed'
//[c]  used by timer queue
	this.timeToNextFrame 	= 0;				// time is in game frames (around 30 thousands of a second)
//[cf]

}
//[of]:w(id)
//[c]// call w("id")
function w(id) 							
{	if(id.indexOf("str")==0) 					
	{	for(var n=0; n<41; n++)				// find stranger
		{	if(ww[n].id ==id)return ww[n];
		}
	}else									
	{	for(var m =41; m < ww.length; m++)		// find others
		{	if(ww[m].id ==id)return ww[m];
		}
	}
	if(ww[0].id ==id) return ww[0]; 		// in case game itself is called. Returns zero: useful error message
	return null;
}
//[cf]
//[of]:speech blocks
//[c]  fills   'object.talkto'  and   hotspots
//[c]
function speechBlock()
{	this.str			= "";
	this.sayFrom		= 0;		// when can these things be triggered?
	this.sayTo			= 999;
	this.restrictions	= 0;		// for other restriction. e.g. "if-then"
}	
//[cf]
//[of]:isPerson
function numIsPerson(num)
{	if(num <0) return 0;
	if(num >=ww.length) return 0;
	return 1;
}
//[cf]
//[c]

	
//[cf]
//[of]:rooms
//[of]:create
//[of]:hotspots

function hotspot()						// html is not constructed until it is read
{	this.num		= 0;
	this.name		= "";				// game can change this, e.g. when it becomes an exit
	this.say		= new Array();
	this.sayAway	= "";
	this.xyArray	= new Array();
	this.walkto		= [0,0];
	this.type		= 0;				// exit?
}

//[cf]
//[of]:update actors
function updateActors()
{	for(var n =0; n < chapters.length; n++)
	{	if(chapters[n].state ==1)
		{	addActorsForChapter(n);
			chapters[n].state =2; 
		}
	}
}
//[cf]


//[c]  add default room
function addRoom(name)								// only used at start
{	var room	 		= new rm();
	room.num 			= rooms.length;
	room.name 			= name; 		
	rooms.push(room);
	return room.num;
}

//[c]  contents
function rAddToRoom(id,x,y)				// same variables as 'add'
{	var myEvent 		= new nE(); 	// event object, defined in 'timer queues'
	myEvent.id		= id;	
	myEvent.x 			= x;				
	myEvent.y 			= y;				
	this.needs.push(myEvent);			// 'this' = rooms[n]
}

function rAddHotspot(name,xyArray,sayAway)
{	var myHot 			= new hotspot();
	myHot.name 			= name;
	myHot.xyArray 		= xyArray;
	myHot.sayAway		= sayAway;
	var toSay  			= arguments[3]; if(toSay !=null) myHot.say.push(toSay);
	toSay  				= arguments[4]; if(toSay !=null) myHot.say.push(toSay);
	toSay  				= arguments[5]; if(toSay !=null) myHot.say.push(toSay);
	this.hotspots.push(myHot);
}
//[c]  other variables are simple to add directly - e.g. rooms[n].variable

//[cf]
//[of]:change room
//[of]:do it now
function changeRoomNow(n)							// triggered by queue
{
//[c]  needed for calculating people positions etc
	floorEdges 	= rooms[n].floorEdges;				// we could just refer to 'rooms[thisRoom].floorEdges...
	floorScale 	= rooms[n].floorScale;				// ... but it is frequently called, so this looks neater
	lastRoom 	= thisRoom; 	thisRoom = n;
	if(rooms[n].darkRoom)
			textColor[0] = darkTextCol[0];
	else	textColor[1] = darkTextCol[1];
//[c]  people etc.
	if(rooms[n].needs.length >0)
	{	for(var i=0; i < rooms[n].needs.length; i++)	
			addNow(rooms[n].needs[i].id, n, rooms[n].needs[i].x, rooms[n].needs[i].y);
	}	
	addStrangers(rooms[n].strangers);
//[c]		hotspot html:			 
//[c]<map name="mapname"> <area shape="polygon" onmouseover="dMouseText('name'); "onmouseout="dMouseText('') "coords="0,0" /> </map>
	var mapString ="<map name=\"hotspotMap\">"; 	
	if(rooms[n].hotspots.length >0)
	{	for(var j=0; j < rooms[n].hotspots.length; j++)	
		{	var html 	= "<area shape=\"polygon\" onClick=\"clickHotspot('"+rooms[n].hotspots[j].name+"')\"";
			html 		= html +" onmouseover=\"dMouseText('"+rooms[n].hotspots[j].name+"')\" onmouseout=\"dMouseText('')\"coords=\"";
	
//[c]	add  each coordinate
			for(var k=0; k+1 < rooms[n].hotspots[j].xyArray.length; k++)				
			{// k+1: stops before last entry, because the last entry wants no comma
				html 	= html + rooms[n].hotspots[j].xyArray[k] +",";
			}	html 	= html + rooms[n].hotspots[j].xyArray[rooms[n].hotspots[j].xyArray.length -1]; //no comma
//[c]	finish off
			mapString =mapString +html + "\">";	
		}
	}		
	mapString = mapString +"</map>";
	fillSpan("SPhotspotMap",mapString);
//[c]  update 'visited'
	rooms[n].visited++;
}



//[cf]


function getRoomImg(room)
{	if(room ==0) return "images/icons_etc/book/loading_screen.jpg";
	return "images/rooms/room"+room+".jpg";
}		



function changeRoom(room)							// adds to queue: when queue is read, calls 'changeRoomNow'
{	stopTalkAndGUIs();
	var img =getRoomImg(room);
	if(pageLoaded) fadeWindowIn(img);				// anything timed is added as a separate event
	removeAll();
			var myEvent 	= new nE(); 
			myEvent.eType 	= "room";
			myEvent.room 	= room;
			myEvent.time 	= 0;
			push2(timeQueue, myEvent);
	add(starId);
	if(pageLoaded) addBack(img);					// back is added in index.html at the start
	if(pageLoaded) fadeWindowOut();
	walkOn(starId);
	sayWhenEnter();
}



//[cf]
//[c]  all these variables are fixed at the start

function rm() 									// rm = room
{
//[c]  number, etc
	this.num 			= 0; 					// set to rooms.length when each room is added
	this.name 			= ""; 		
	this.floatDown		= 0;					// added in final 'calculate XYZ' stage
	this.darkRoom		= 0;					// for using dark sprites
	this.mapXY			= [0,0];				// position of label on map: [0,0] = don't show it.
//[c]  fill it up: but no background image (caculated separately)
	this.strangers		= -1;					// how many random strangers to add?
	this.needs 			= new Array();			// people etc. (mO objects)
	this.hotspots		= new Array();
	this.hotspotTalk	= new Array();			
	this.floorEdges 	= [80,240,560,480];
	this.floorScale 	= [0.5,1.0];
//[c]  visiting
	this.unlockedBy 	= -1;					// completing this chapter opens the room
	this.visited 		= 0;  					// increments with every visit
	this.defaultEnter	= [-50,479, 90,479];	// where to enter and walk on
	this.sayOnEnter		= new Array();			// can say a different hing each time if you wish
//[c]  functions
	this.addToRoom		= rAddToRoom;			// stores variables needed for 'add'
	this.addHotspot		= rAddHotspot;
}


function roomByName(name) 						// for exit purposes etc.
{	for(var n =0; n < rooms.length; n++)
	{	if(rooms[n].name ==name)return n;
	} return -1;
}


//[c]  ***EXITS*** - with less reliance on mouseover or cursor (and no obvious hotspot numbering) exits are just defined by name.
//[c]  				rely on space bar equivalent instead.

//[cf]
//[of]:image html
//[of]:image queue
//[c]  image on load 
function imgOnload(url)
{	removeFromQueue(url); 
}
//[c]  handle the queue
function cleanDownloadQueue()
{	for(var n=0;n<4;n++)
	{	downloadQTime[n]=downloadQTime[n] +1; 
		if(downloadQTime[n] >2)					// waited too long? Clear it anyway.
		{	downloadQTime[n] =0;
			downloadQueue[n]="";
		}
	} 
}function addToDownloadQueue(url)
{	for(var n=0;n<4;n++)
	{	if(downloadQueue[n] =="")
		{	downloadQueue[n] =url; 
			downloadQTime[n] =0; 
			return 1;
		}
	}return 0;
}
function removeFromQueue(url)
{	for(var n=0;n<4;n++)
	{	if(downloadQueue[n] ==url)
		{	downloadQueue[n] =""; 
			downloadQTime[n] =0; 
			return 1;
		}
	}return 0;
}
//[cf]
//[of]:fill span
function fillSpan(spanName,myString)
{	var doIt =0; 
	if(!pageLoaded) doIt =1;										// constantly tested, so should be safe
	if (document.getElementById(spanName))doIt =1;					// don't risk writing null object
	if(doIt) document.getElementById(spanName).innerHTML =myString; // span exists? then use it
	return doIt;
}
//[cf]
//[of]:make strings
function makeImgString(who,url,z,wide,high,altText)// who, not id, to fit with html
{
//[c]  below window: many images. Above window: same image each time.
	var imgString 	= "<img onload=\"imgOnload('"+url+"')\" src= \""+url+"\"";
	if(z >windowZ) 
		imgString 	= "<img src= \""+url+"\""; // don't bother with queue for this single image
//[c]  below window: now clicks. Above window: clicks.
	var eventString = "";
	if(z >windowZ) 
		eventString = " onmouseover=\"dMouseText('"+altText+"')\" onmouseout=\"dMouseText('')\" onClick=\"clickObject("+who+")\" onMouseDown=\"clickObject("+who+")\" onMouseUp=\"clickObject("+who+")\"";
//[c]  note multiple click redundancy: browser can easily miss clicks while animating
	var styleString = " style=\"border: 0px solid;";
//[c]  all images want no border. Some also specify width and height.
	if(wide && high) 
		styleString = styleString +" height: "+high+"px; width: "+wide+"px; \">";
	else			  styleString =styleString +"\">";
	var picString 	= imgString +eventString +styleString;
	return picString;
}

//[c]  div gives absolute position
function makeDivString(id,x,y,z)
{	var width  	= arguments[4];if(width ==null) width =0;
	var height 	= arguments[5];if(height ==null) height =0;
	var sizeString ="";
	if(width)sizeString  = " width:" +width +"px;"; 
	if(height)sizeString = sizeString +" height:" +height +"px;"; 
	var divString 	= "<div id= \""+id+"\" style= \"position: absolute; top: "+y+"px; left: "+x+"px;"
						+sizeString +" z-index: " +z +" \">";
	return divString;
}
//[cf]
//[of]:window +back
//[c]  style="opacity:0.4;filter:alpha(opacity=40)"

function addWindowNow(url)// 0=default. 1 =add talking on top
{	var opac  	= arguments[1]; if(opac ==null)opac =1.0; // opacity
	var opac100 = opac *100;
	document.getElementById("SPhotspotWindow").innerHTML = "<div id=\"hotspotWindow\" class=\"unselectable\" unselectable=\"on\" style=\"opacity:"+opac+";filter:alpha(opacity="+opac100+"); position: absolute; top: 0px; left: 0px; z-index: 999999\"><a onClick=\"clickBackground()\"><img src=\""+url+"\" style=\"border: 0px solid; height=480; width=640;\" usemap=\"#hotspotMap\"></a></div>";
}

function addBackNow(url)
{	document.getElementById("SPbackground").innerHTML = "><div id=\"background\" style=\"position:absolute; top:0px; left:0px; z-index:999\"><img src=\""+url+"\";style=\"border:0px solid; width=\"640\" height=\"480\"></div>";
}
//[cf]


function oUpdateBothPics(type)// 0=default. 1 =add talking on top
{	if(this.room !=thisRoom)return;
	if(this.autoscale)
	{	if(this.nextStep[0] || this.nextStep[1])		// not just 0,0
		this.scaleNow = getScaleAt(this.feet[1]);
	}
//[of]:calculate XYZ
	var scaledOffset =[0,0];
	var scaleBy 	= this.scaleNow;
	if(this.who <41) scaleBy = scaleBy *4;	// strangers are 1/4 size (only use in small rooms!)
	var scaledFloat = scaleIt(rooms[thisRoom].floatDown,scaleBy);	// in some rooms your feet are not visible
	scaledOffset[0] = scaleIt(this.feetOffset[0],scaleBy);
	scaledOffset[1] = scaleIt(this.feetOffset[1],scaleBy);
	this.picXYZ[0] 	= this.feet[0] - scaledOffset[0];
	this.picXYZ[1] 	= this.feet[1] - scaledOffset[1] + scaledFloat;
//[c]  calculate size
	this.picSize[0] = scaleIt(this.size[0],scaleBy);
	this.picSize[1] = scaleIt(this.size[1],scaleBy);
	if(this.faceImg !="")
	{	this.facePicSize[0] =scaleIt(this.faceSize[0],scaleBy);
		this.facePicSize[1] =scaleIt(this.faceSize[1],scaleBy);
	}
//[c]  calculate clickable
	this.picCXYZ[0] = this.picXYZ[0] + scaleIt(this.picSize[0],0.3); // 0.3 gap, 0.4 clickable, 0.3 gap
	this.picCXYZ[1] = this.picXYZ[1] + scaleIt(15, scaleBy);
	this.picCSize[0] = scaleIt(this.picSize[0],0.4);
	this.picCSize[1] = scaleIt(this.picSize[1],0.75);
//[c]   z values:    1000 per line   +  10 per person (100 people per line)   +   body=4, face=5
	if(type ==0)	// normal image
	{	this.picXYZ[2] = 
			(1000 *this.feet[1])+ (this.who *10) + 4;	
//[c]  clickable image: 1 million higher (1000 lines of 100 people of 10 layers each)
		this.picCXYZ[2] = this.picXYZ[2] +1000000;
	}
	
		
//[cf]
//[c]  write HTML							DivString (id, x, y, z)
//[c] 										ImgString (who, url, z, wide, high, altText)
	var mainDivString 	   	= makeDivString(this.id, 
											this.picXYZ[0], this.picXYZ[1], this.picXYZ[2]);
	var mainPicString 	   	= makeImgString(this.who, this.img, 
											this.picXYZ[2], this.picSize[0], this.picSize[1], "");
	var clickableDivString 	= makeDivString(this.id +"C", 
											this.picCXYZ[0], this.picCXYZ[1], this.picCXYZ[2]);
	var clickablePicString 	= makeImgString(this.who, blankPic, 
											this.picCXYZ[2], this.picCSize[0], this.picCSize[1], this.name);
	var myString 			= mainDivString 
							+ mainPicString
							+"</div>"
							+ clickableDivString
							+ clickablePicString 
							+"</div>";
//[c]  add a face on top?
	if((this.id ==talker) && (this.mouthImg !=""))	// facePicSize alredy updated by findXYZ
	{	var faceDivString  = makeDivString(this.id +"Mouth",
											this.picXYZ[0],this.picXYZ[1],this.picXYZ[2] +1);
		var facePicString  = makeImgString(this.who,this.mouthImg,
											this.picXYZ[2]+1,this.facePicSize[0],this.facePicSize[1],"");
		myString =myString +faceDivString +facePicString  +"</div>";
	}
	fillSpan("SP"+this.id,myString);
}
//[cf]
//[c]  check every frame:
//[of]:COSTUMES ------
//[of]:costume object
function costumeLoop()
{	this.baseURL 	= ""; 		  
	this.numbers 	= new Array();		// baseURL: add "number.gif" to make it a full URL
	this.frame 		= 0;			  
	this.allLoaded 	= 0;				// flags if every image has been loaded at least once
	this.delays 	= new Array(); 
	this.events 	= new Array();		//events = sound or anything triggered by a particular frame
	this.size 		= [0,0];		  
	this.feetOffset = [50,250];			// width and height; feet in relation to top left of picturte
	this.copyLoop	= cosCopyLoop;
}
//[of]:copy loop
function cosCopyLoop(loop)
{	if(loop ==null) return;
	this.baseURL 	= loop.baseURL;
	this.numbers 	= loop.numbers;
	this.frame 		= 0;				// new costume? Start from first frame.
	this.allLoaded 	= loop.allLoaded;
	this.delays 	= loop.delays;
	this.events 	= loop.events;
	this.size 		= loop.size;
	this.feetOffset = loop.feetOffset;
}
//[c]  to copy a costume, just use the clone tool
//[cf]

//[c]
function costume()
{	this.baseDelay	= 2;									// f=front, fl=frontleft, etc.
	this.f 			= new costumeLoop(); 
	this.fl 		= new costumeLoop(); 
	this.l 			= new costumeLoop(); 
	this.bl 		= new costumeLoop();
	this.b 			= new costumeLoop(); 
	this.br 		= new costumeLoop(); 
	this.r 			= new costumeLoop(); 
	this.fr 		= new costumeLoop();
}
//[c]
function wardrobe()											// convenient place to store unused costumes
{	this.costumes		= new Array();
	// space for other objects if you ever need them
}

//[c] to copy a wardrobe, use 	var obj2= new cloneObject(obj1);
//[cf]
//[of]:getLoop
function oGetLoop(costume,layer)
{	if(costume ==null)return null;
	var loop =costume.f;
//[c]  find the loop
	switch(this.dir)
	{	case "fl":loop =costume.fl; break;
		case "l" :loop =costume.l;  break;
		case "bl":loop =costume.bl; break; 
		case "b" :loop =costume.b;  break;
		case "br":loop =costume.br;	break; 
		case "r" :loop =costume.r;  break;
		case "fr":loop =costume.fr; break; 		  
	}
//[c]  just in case: that loop doesn't exist?
	if(loop ==null){loop =costume.f;
	if(loop ==null){loop =costume.b;
	if(loop ==null){loop =costume.l;
	if(loop ==null){loop =costume.r;
	if(loop ==null)return null; }}}}
//[c]  for later: is it worth checking every frame?
	this.animating =1;											// assume you need to check
	if(loop.numbers.length <2) 									// only one frame or less?
	{	if(  (this.dir !="")								// no direction? Carry on(probably just started)
		   ||(this.targetXYDir ==this.targetXYDir))				// no need to turn?
				{	if(  (  (this.targetXY[0] ==0)
						  &&(this.targetXY[1] ==0)	)			// just started?
					   ||(	(this.targetXY[0] ==this.feet[0])
						  &&(this.targetXY[1] ==this.feet[1]) )	// or not waiting to walk?
			  		   )
			  		    	this.animating =0;					// don't waste time checking then
		}										
	}
//[c]  inherit its values
	if(layer ==4)// main image
	{	this.size[0]       =loop.size[0];
		this.size[1]       =loop.size[1];	
		this.feetOffset[0] =loop.feetOffset[0];	
		this.feetOffset[1] =loop.feetOffset[1];
	}
	if(layer ==5)// face
	{	this.faceSize[0]   =loop.size[0];
		this.faceSize[1]   =loop.size[1];	
	}
	return loop;
}

//[cf]
//[of]:change costume
//[c] COMPLEX: use a string to summarise all the changes
//[c] 	don't try to standadize - costume changes are always non-standard
function changeCostume(which,dir)			// stored as a single event, so cannot sit - WAIT - stand etc.
{	var myEvent 		= new nE(); 	
	myEvent.eType 		= "cosChange";
	myEvent.codePile	= which; 
	myEvent.dir 		= dir;
	myEvent.time 		= 0; // normally instantly after previous events
	push2(timeQueue, myEvent);
}
//[c]
function changeCostumeNow(which,dir)
{	switch(which)						// start it?
	{	case "saintSit": 
		w("cmHero").wardrbs["myWard"].costumes["cos_idle"].r.copyLoop(w('cmSaint').wardrbs["c00"].costumes["cos_sitidle"].r);
		w("cmHero").wardrbs["myWard"].costumes["facetalk"].r.copyLoop(w('cmSaint').wardrbs["c00"].costumes["cos_sittalk"].r);
		w("cmHero").animating 	= 1;	// tells regular animation loop to take over
		w("cmHero").restricted  = 1;	// cannot walk to friend (keep it simple!)
		w("cmHero").dir = dir;
		break;
		case "saintStand": 
		w("cmHero").wardrbs["myWard"].copyWardrobe(w("cmHero").wardrbs["c19"]);
		w("cmHero").animating 	= 1;	
		w("cmHero").restricted  = 0;
		break;
	}	
}

//[cf]
//[of]:EVERY FRAME
//[c]
function oCheckCostume()							// called every frame by game engine
{	if(this.room !=thisRoom)return 0;
	if(this.animating ==2)return 0;					// 2 = special animation overrides this
	if(!this.animating)								// 'animating' is reset when getting a loop
	{	{	if(this.img !="")						// if you already have an image don't waste any more time
			{	if(talker !=this.id)return 0;
			}
		}
	}		
	this.timeToNextFrame =this.timeToNextFrame -1;	// count down
	if(this.timeToNextFrame <1)						// time to act?
	{	
//[of]:stop cos?
//[c]  at target?
	var toWalk 			= 0;							// need to walk?
	if(this.targetXY[0] !=this.feet[0])					// not where you want to be? Then walk!
		toWalk 			= 1;						
	if(this.targetXY[1] !=this.feet[1])
		toWalk 			= 1;
	if((this.targetXY[0] ==0)&&(this.targetXY[1]==0))	// oh, wait, still at default? False alarm.
		toWalk 			= 0; 	
//[c]  should not walk anyway?
	if(this.restricted == 1) 							// sitting down? Same scene, no need to get up.
		toWalk 			= 0; 	
	if(this.id ==talker)
		toWalk 			= 0;
//[c]  prevent them starting again
	if(!toWalk)
	{	this.targetXY 	= [0,0];
		this.targetXYDir = "";							// otherwise animation stops to let you turn
	}
//[c]  check timer
	var wasWalking 		= false;						// also used later
	if(timeQueue[0] !=null)						// in the middle of a timed event?
	{	if(timeQueue[0].eType =="walk")			// may be on another person's timer
		{	wasWalking 	= true; 
			if(!toWalk)
				timeQueue[0].time = 0;			// reset their timer queue
		}
	}
//[cf]
//[of]:turn cos?

	if(  (this.targetXYDir !="")
	   &&(this.targetXYDir !=this.dir))			// need to turn first?
		{	turnInstNow(this.id, getPartTurn(this.dir,this.targetXYDir))// includes updateBothPics
			this.timeToNextFrame =4;
			return 1; 							// leave it to the timer queue to plot still images
		}
		else this.targetXYDir ="";				// must be the same direction. So reset.

// TO DO: if no direction, set directio as target direction? OR will this just create bugs?
//[cf]
//[of]:walk cos?
if(toWalk)										// 'talking' overrides this (already set this to zero)
{	var difX =this.targetXY[0] -this.feet[0];	// still here? Not stationary, turning or idling then.
	var difY =this.targetXY[1] -this.feet[1];	// how far to step?

//[of]:step size
	var yProportion =1; 
	if(difX !=0)
		yProportion = difY/difX;						// avoid divide by zero errors!
//[c]  speed
	var speed 		= this.speed;
	if((yProportion >2)||(yProportion <-2))				// slower if mostly in the y direction?
	speed 			= scaleIt(speed,0.5);				// don't worry, 'scaleIt' will not return zero
	speed 	  		= this.adjustSpeed(speed);
//[c]  slow down X and Y so angular speed is right
	var totalPath 	= (difX * difX) + (difY * difY);	// Pythagoras
	totalPath 		= Math.sqrt(totalPath);				// length of line if speed did not matter
	var numSteps 	= totalPath/speed;
	difX 			= Math.round(difX /numSteps);
	difY 			= Math.round(difY /numSteps);
//[c]  proportion must stay the same (so direction stays same), but scale can change
	this.nextStep[0] = this.feet[0] +difX;
	this.nextStep[1] = this.feet[1] +difY;
//[cf]

//[of]:too far?
	if(difX >0)
	{	if(this.nextStep[0] >this.targetXY[0]	)
			this.nextStep[0] = this.targetXY[0];		// overstepped to right?
	}else
	{	if(this.nextStep[0] <this.targetXY[0]	)
			this.nextStep[0] = this.targetXY[0];		// overstepped to left?
	}if(difY >0)
	{	if(this.nextStep[1] >this.targetXY[1])
			this.nextStep[1] = this.targetXY[1];		// overstepped below?
	}else 
	{	if(this.nextStep[1] <this.targetXY[1])
			this.nextStep[1] = this.targetXY[1];		// overstepped above?
	}		  	
//[cf]

//[c]  update
	if(this.nextStep[0] && this.nextStep[1])	// one final check - not just 0,0?
	{	this.feet[0] =this.nextStep[0]; 
		this.feet[1] =this.nextStep[1]; 
	}
}	

//[cf]
//[of]:find image
//[c]  which loop?
	var cos 		= this.wardrbs["myWard"].costumes["cos_idle"];		// same for talking (facetalk is just the mouth)
	if(toWalk)cos 	= this.wardrbs["myWard"].costumes["cos_walk"];
	if(cos ==null) 		return 0;
	var loop 		= this.getLoop(cos,4);			// layer 4 = main image. Automatically updates size
	if(loop ==null) 	return 0;
	var url 		= loop.baseURL;
	if(url =="") 		return 0;
	
//[c]  frame => image
	var frame 		= loop.frame;
	if(!toWalk && wasWalking) frame =0;				// previously in middle of walk? Start again.
	if(this.id ==talker) frame =0; 					// talking always uses idle frame 0
	if(loop.numbers[frame] ==null) return 0;
	this.img =url +loop.numbers[frame] +".gif";
//[c]  delay til next time
	var delay 		= cos.baseDelay;
	if(this.id ==talker)delay =4; else
	{	if(loop.delays[frame] !=null)
			delay 	= delay +loop.delays[frame];	// default delay plus/minus extra for that frame
		if(!loop.allLoaded)							// initial download: allow extra time for images
		{	if(delay <4)delay =4;	}	
	}this.timeToNextFrame =delay;
//[c]  increment frame for next time
	frame++;
	if(frame >=loop.numbers.length)
	{	loop.allLoaded = 1;							// flag as 'every frame has downloaded once'
		frame 		= 0;
	}
	loop.frame =frame;								// remember the position for next time

//[cf]
//[of]:mouth etc
//[c]  extra layers: mouth, face, wings, etc.
//[of]:wings
/*
//--------------------------------------------------------------------------------------------------------------
function addWings( who, backWings, frontWings, xOffset, yOffset)
{	if((who <1)||((who +2) >CHARACTERS_TOTAL))  return 0;
	if(character[who].Room !=thisRoom) return;
	var scale = GetScalingAt(character[who].x,character[who].y);
	xOffset = (xOffset * scale)/100;
	yOffset = (yOffset * scale)/100;
	character[backWings].ChangeRoom(character[who].Room,character[who].x + xOffset,character[who].y + yOffset);
	character[frontWings].ChangeRoom(character[who].Room,character[who].x + xOffset,character[who].y +1);
	character[frontWings].z = -yOffset;
	character[frontWings].ManualScaling = 1	character[frontWings].Scaling = scale;
	character[backWings].ManualScaling = 1	character[backWings].Scaling = scale;
}
*/
//[cf]
if(this.id ==talker) // grab face image (if any) then carry on as before
{	
//[c]  get mouth loop details
	cos 				= this.wardrbs["myWard"].costumes["facetalk"];
	loop 				= this.getLoop(cos,5);			// layer 5 = mouth. getLoop automatically updates size.
	url 				= loop.baseURL;
	this.timeToNextFrame =4;						
//[c]  random image  												Math.floor(Math.random()*11) returns 0-10
//[c]  																extra frame =mouth closed. 'length' adds 1 (starts at 1, not 0), but 'random' subtracts 1.
	var nn 				= loop.numbers.length +1;
	frame 				= Math.floor(Math.random()*nn)
	var newMouthImg 	= "";
	if(frame < loop.numbers.length)
		newMouthImg 	= url +loop.numbers[frame] +".gif";
//[c]  not same mouth image twice?
	if(newMouthImg ==this.mouthImg)						// same as previous image?
	{	frame 			= Math.floor(Math.random()*nn)	// try again. Occasional repeat is OK, but not too often.
		newMouthImg 	= ""; 
		if(frame < loop.numbers.length) 
			newMouthImg = url +loop.numbers[frame] +".gif";
	}
	this.mouthImg 		= newMouthImg;
}
//[c]  no talking? No mouth.
else	this.mouthImg	= "";
	
	
//[cf]

//[c]  computer is not overloaded?
	if(!addToDownloadQueue(this.img))			// no space to download?	
	{	this.timeToNextFrame = 0;				// forget the delay
		return;									// try again next loop
	}
//[c]  then paste this image!
	this.updateBothPics(0);
	}
}
//[cf]
//[cf]
//[of]:TIME QUEUE ------
//[c]  WARNING: each function is read all at once, the first item in EACH PERSON'S queue runs IMMEDIATELY
//[of]:event object
function nE() 					// nE = next Event (old "event" is now called "chapter")
{	this.time 		= 0;	
	this.started 	= 0;		// time delay in game frames: first item counts down until used and removed
	this.id			= "";		
	this.restrictions = 0;		// restrictions: e.g. walk anywhere?
	this.eType 		= "";		// eType = "talk", "walk" etc.		
	this.room		= 0;		
	this.dir		= "";
	this.opac		= 0;
	this.codePile	= "";		// cldoe block, but note it is all called instantly
//[c]  talk
	this.string 	= "";
	this.font	 	= 1;
	this.color	 	= darkTextCol[0];	
	this.fontSizeAdd = 0;
 	this.textWidth	= 300;	
 	this.sourceLoop	= null;
 	this.yourLoop	= null;
//[c]  images
	this.img	 	= "";
 	this.offsetX	= 0; 
 	this.ofsetY		= 0;
 	this.x 			= 0; 
 	this.y 			= 0;
}

function push2(array, event)
{//	var what =event.eType; if(what =="talk")what = what +", "+event.string;
//	allert("pushing '"+ what+"' to array");
	array.push(event);
}
//[cf]
//[of]:(testing)
//[c]  for testing
function testShowQueue(id)
{	var str ="talker ='"+talker 
						+"'; img ="+w("cmHero").wardrbs["myWard"].costumes["facetalk"].r.baseURL +"...";

	if((id ==null)||(id =="")) id =starId;
	if(w(id) !=null)
		str =str +"<br>id ="+id +"; dir ='"+w(id).dir +"'; timeToNextFrame = "+w(id).timeToNextFrame +"<br>Timer queue:";
	for(var i =0; i < timeQueue.length; i++)
	{	str = str +"<br>" +i +": "+timeQueue[i].id 
				  +"; type=" +timeQueue[i].eType 
				  +"; time=" +timeQueue[i].time 
				  +"; dir='" +timeQueue[i].dir 
				  +"'; str='" +timeQueue[i].string 
				  +"'; x='" +timeQueue[i].x+"'";
	}
	debugMessage(str);
}

	
//[cf]
//[of]:stop everything
//[c]  when clicking, or starting cutscene

	function oStopWalk()
	{	if((this.targetXY[0] ==0)&&(this.targetXY[1] ==0))
			return 0;	// not walking
		this.targetXY 		= [0,0];
		this.targetXYDir 	= "";
		return 1;
	}
//[c]	
	function stopAllWalks()
	{	for(var n=0; n< ww.length; n++)				
		{	if(ww[n].room ==thisRoom) 
				return ww[n].stopWalk();
		}	return 0;
	}
//[c]	
	function stopEverything(id) 			
	{	for(var i =0; i < timeQueue.length; i++)
			timeQueue.shift();					// remove everything from the queue!
		stopTalkAndGUIs();						// includes redraw for talker
		sponCount 					= 0;
		if(id !=null)
		{	if(w(id) !=null)
				return w(id).stopWalk();
		}		
		stopAllWalks();
	}

//[cf]
//[c]  called every frame by game engine
function checkTimeQueue()					// immediately plays first event, then waits for timer
{	testShowQueue();
	if(GUIon) return;
//[of]:start
//[c]  OK to go?
	if(timeQueue[0] ==null)return;			// no timer there?
//[c]  only just started?
	var a 			= timeQueue[0];			// for shorter code 
	if(!a.started)
	{	a.started 	= 1; 					// in progress: next time skip the starting code
//[c]  NO CLOSED BRACKETS - this starts the main function
											
//[cf]
	switch(a.eType)						
//[c]  people
	{	case "talk": 	startTalk		(a.id, a.string, a.font, a.color, a.fontSizeAdd, a.textWidth, a.x, a.y); break;
		case "frame":	useFrameNow		(a.id, a.img, a.x, a.y, a.offsetX, a.offsetY);	break;
		case "add":		addNow			(a.id, a.room, a.x, a.y);						break;
		case "remove":	removeNow		(a.id);											break;
		case "opacity":	opacityNow		(a.id, a.opac);									break;			
		case "wait": 	break; 			
		case "turn": 	queuePartTurns	(a.id, a.dir); 									break;
		case "turnXY":	queuePartTurns	(a.id, whereToTurn(a.id, a.x, a.y));			break;
		case "turnInst":turnInstNow		(a.id, a.dir);									break;
		case "walk": 	startWalk		(a.id, a.x, a.y, a.restrictions);				break;
		case "walkOn": 	walkOnNow		(a.id, a.x, a.y);								break;
//[c]  not a person
		case "endCut":  break;			// hit escape (or escape button): jump past other events to this one
		case "window":	addWindowNow	(a.img, a.x); 									break;		
		case "back":	addBackNow		(a.img); 										break;		
		case "clear":	removeAllNow	();												break;
		case "room": 	changeRoomNow	(a.room);										break;
//[c]  complex 
		case "cosChange": changeCostumeNow	(a.codePile, a.dir);							break;
	}
  }
//[of]:finish
//[c] overrulled events?
	if(a.id ==null)								// look at the person who acts
	{	a.id = this.id;
		if(a.id !=null)
		{	if(w(a.id).restricted == 1)			// cannot turn or walk?
			{	if(a.eType =="walk")a.time =0;
				if(a.eType =="turnInst")a.time =0;	
			}
	}	}		
//[c]  count down
	a.time--; 
	if(a.time >0)return;								// nothing more to do. Still counting down.
	
//[c]   still here? Tidy up
	switch(a.eType)										// some actions need final actions
	{	case "talk": stopTalkAndGUIs(); 	break;
	}
		
//[c]   remove the event
	timeQueue.shift();									// next event (if any) is now at front of queue
		
//[cf]
}
//[cf]
//[c]  actions:
//[of]:scale
function oScale(toWhat)
{	this.scaleNow 	= toWhat; 
	this.updateBothPics(0);
}

function getScaleAt(y) 	// top: floorEdges[1],floorScale[0];  bottom: floorEdges[3],floorScale[1]
{	var floorHeight = floorEdges[3] -floorEdges[1];
	var yFromTop 	= y -floorEdges[1];
	var proportion 	= yFromTop/floorHeight;
	var maxDifference =floorScale[1]-floorScale[0];
	var result 		= floorScale[0] + (proportion * maxDifference);
	if(result <0.05)	// probably can't see smaller than 5%
		result 		= 0.05;
	return result;
}

//[cf]
//[of]:add, remove
//[c]  design scenes like theater: normally enter from the wings!
//[of]:add

function add(id)// only changes position: the rest is done by 'changePic'
{	var room 	= arguments[1]; if(room ==null) room =0;
	var x 		= arguments[2]; if(x ==null) x =0;
	var y 		= arguments[3]; if(y ==null) y =0; 
	var myEvent 		= new nE(); 	
	myEvent.eType 		= "add"; 		
	myEvent.id			= id;	
	myEvent.x 			= x;				
	myEvent.y 			= y;				
	myEvent.room 		= room;	
	myEvent.time 		= 0; // normally instantly after previous events
	push2(timeQueue, myEvent);
}

function addStrangers(howMany)
{	if((howMany ==null)||(howMany ==0)) howMany =20;
	if(howMany >40) howMany =40;
	var left 	= floorEdges[0];
	var width 	= floorEdges[2] -left;
	var top 	= floorEdges[1];
	var height 	= floorEdges[3] -top;
	for(var i =1; i <= howMany; i++)
		add(ww[i].id, thisRoom, left +Math.floor(Math.random()*width), top +Math.floor(Math.random()*height));
}
//[cf]
//[of]:add now
function canGoThereNow(id,rum)
{	var can =1;
	if(!chapterDone(rooms[rum].unlockedBy)) can =0;
	if(w(id).restricted)can =0;
	return can;
}

function addStarNow()
{	if(canGoThereNow(starId,thisRoom))add(starId);
}

function addNow(id,room,x,y)					// only changes position: the rest is done by 'checkCostume'
{	var object 				= w(id);
	if(object ==null) return;
	if(room ==0) room 		= thisRoom;
	if(!x && !y)
	{	x = rooms[thisRoom].defaultEnter[0];
		y = rooms[thisRoom].defaultEnter[1];
	}
	if(canGoThereNow(id,room))
	{	object.room 		= room; 
		object.feet[0] 		= x; 	
		object.feet[1] 		= y;
		object.animating	= 1;				// forces 'checkCostume' to look for the frame
		if(room ==thisRoom)
		{	object.getLoop(object.wardrbs["myWard"].costumes["cos_idle"],4); // layer 4 =main. getLoop automatically updates size.
			object.scaleNow = getScaleAt(object.feet[1]);
			object.checkCostume();			// includes 'updateBothPics'
		}	
	}	
		
}

//[cf]
//[of]:remove
function remove(id)
{	var myEvent 		= new nE(); 	
	myEvent.eType 		= "remove"; 		
	myEvent.id		= id;	
	myEvent.time 		= 0; // normally instantly after previous events
	push2(timeQueue, myEvent);
}
//[c]
function removeNow(id)
{	var object =w(id);
	object.room 		= -1; 
	object.animating 	= 0; 
	object.targetXY 	= [0,0]; 
	object.feet 		= [0,0]
	document.getElementById("SP"+object.id).innerHTML =""; // no HTML there now
}

function removeAllNow()
{	var n =40; 
	for(n =41; n < ww.length; n++)
	{	if(ww[n].room ==thisRoom)removeNow(ww[n].id);
	}
	if(ww[1].room ==thisRoom)				// if 1 stranger is here, they might all be
	   for(n =0; n<41; n++)
	   {	if(ww[n].room ==thisRoom)removeNow(ww[n].id);
	   }
}
//[cf]


function walkOn(id)
{	var x = arguments[1]; if(x ==null) x =0;
	var y = arguments[2]; if(y ==null) y =0; 
	var anywhere = arguments[3]; if(anywhere ==null) anywhere =0; 
	var myEvent 		= new nE(); 
	myEvent.eType 		= "walkOn"; 
	myEvent.id 			= id; 
	myEvent.x 			= x; 
	myEvent.y 			= y; 
	myEvent.restrictions = anywhere;
	myEvent.time 		= 10;		// see "walk": constantly reset; stopped by 'checkCostume'
	push2(timeQueue, myEvent);
}
function walkOnNow(id,x,y,anywhere)
{	if(!x && !y)
	{	x = rooms[thisRoom].defaultEnter[2];
		y = rooms[thisRoom].defaultEnter[3];
	}
	startWalk(id,x,y,anywhere);
}
function sayWhenEnter()
{	if(ww[sn].room !=thisRoom) return;
	if(rooms[thisRoom].sayOnEnter.length >0)
	{	say("",rooms[thisRoom].sayOnEnter[0]);
		rooms[thisRoom].sayOnEnter.shift();
	}
}

function removeAll()
{	var myEvent 		= new nE(); 	
	myEvent.eType 		= "clear"; 		
	myEvent.time 		= 0; // normally instantly after previous events
	push2(timeQueue, myEvent);
}
//[cf]
//[of]:wait
//[c]  default: storystar's time queue
//[c]  WARNING: the first item in each person's queue begins at the START of each function, even if it was not added til the end!

function wait(frames)
{	var id  		= arguments[1];	if((id ==null)||(id =="")) id =starId; 
	w(id).wait(frames,who);
}

function oWait(frames)
{	var id  		= arguments[1];	if((id ==null)||(id ==0)) id =this.id;
	var myEvent 	= new nE(); 
	myEvent.eType 	= "wait";
	myEvent.time 	= frames;
	push2(timeQueue, myEvent);
}
//[cf]
//[of]:talk
//[of]:say or think
//[c]  type: 1 =lookAt(default);  2 =monolog;  3 =monolog+don't fade out;  4 =after GUI (and then fade)

function sayOrThink(id,what)
{	var type 		= arguments[2];if((type ==null)||(type ==0)) type =1;
	if(id =="") id =starId;
	if(ww(id).room ==thisRoom)
	{	say(id,what); 
		return;
	}	
   if(type ==1){	sayBubble(id,what,1); // fade in
                	sayBubble(id,what,2); // say first
//[c]  comment after thinking
  	var n =Math.floor(Math.random()*5) //returns 0-4
	switch(n)						// start it?
	{	case 0: sayBubble(id,"I'm trying to remember something that might help, and I don't think that is it."); break;
		case 1: sayBubble(id,"I thought that might be useful, but evidently not."); break;
		case 2: sayBubble(id,"This isn't helping me with my task."); break;
		case 3: sayBubble(id,"No, that's not what I want."); break;
		case 4: sayBubble(id,"Maybe I should look around a bit more."); break;
	}
//[c]  continue
                	sayBubble(id,what,3); // fade out
                	sayBubble(id,what,4); // just disappear
                }
    if(type ==2){	sayBubble(id,what,0); }// standard in and out
    if(type ==3){	sayBubble(id,what,1); // fade in
                 	sayBubble(id,what,2); // say first
                 	sayBubble(id,what,4); // just disappear
                }
    if(type ==4){	sayBubble(id,what,2); // say first
                 	sayBubble(id,what,3); // fade out
                 	sayBubble(id,what,4); // just disappear
                }
}

//[cf]
//[of]:bubble face
//[c]  'cBubbleface' is a separate character
//[c]  	- with a different loop for each hero -
function checkBubbleFace(id)
{	if(w("cBubbleface").room ==thisRoom)return; // already there?
	if(id =="cmHero")
	{ // change costume
	// change room
	// check z is high enough
	}  
}

function sayBubble(id, what, stage) { // stage 0 =all, 1 =fade in, 2 =talk, 3 =fade out, 4 =remove
  if( ((stage ==0)||(stage ==2)) &&(what ==""))return;
//[c]  fade in
  if((stage ==0)||(stage ==1))
  { // half fade in
  }
//[c]  talk
  if((stage ==0)||(stage ==2))
   w("cBubbleface").say(what);
//[c]  fade out
  if((stage ==0)||(stage ==3))
  { // fade out
  }
}

//[cf]
//[of]:different fonts

function bigFontSay(id,what)
{	var newSize =Math.round(defaultFontSize * 1.5);
	say(id,what,"","",newSize);	// id, what, color, font, fontsize
}

function smallFontSay(id,what)
{	var newSize =Math.round(defaultFontSize * 0.7);
	say(id,what,"","",newSize);
}
//[cf]
//[of]:say1
//[c] say("idname","what to say") or  say("","what to say") 
function say1(id, string, font, color, fontSizeAdd, textWidth, x, y)
{	if(id =="")id =starId; // default: uses sn timer
	var myEvent 		= new nE(); 
	myEvent.eType 		= "talk";
	myEvent.id			= id;		// id "message" means 'start talk' uses the message box instead
	myEvent.string 		= string;
	myEvent.font	 	= font;
	myEvent.color 		= color;
	myEvent.fontSizeAdd	= fontSizeAdd;
	myEvent.textWidth 	= textWidth;
	myEvent.x 			= x;
	myEvent.y 			= y;
//[c]  calculate time
	var seconds 		= (string.length /readingSpeed) +2;
	myEvent.time 		= Math.round(seconds * timePerFrame);
//[c]  add to the end of the queue
	push2(timeQueue, myEvent); // only one 'say' at a time, so all talk is queued in star's timer
}



//[cf]
//[of]:start talk
//[c]   note: nice (though pale) text shadow works in Android, but not IE8. So draw backgrounds accordingly!
function startTalk(id, message, font, color, fontSizeAdd, textWidth, botMidX, botMidY)
{	var y						= 0;
	var z 						= 982999;	
	var shadowColor 			= "White"; 	// font shadow is very pale: need white even for grey rooms
//[of]:check variables

	if((font ==null) ||(font ==0  )) font 	= 1;
	if((color ==null)||(color =="")) color 	= textColor[0];
	if(fontSizeAdd ==null) fontSizeAdd ==0;
	if(font ==2) fontSizeAdd = fontSizeAdd +80;		// large font
	if(font ==3) fontSizeAdd = fontSizeAdd +30;			// handwriting font is naturally smaller
	var fontSize 							= defaultFontSize + fontSizeAdd;
	var length 								= message.length * fontSize * 0.085; // font size 100: letters 8.5 px
	if((textWidth ==null)||(textWidth ==0)) textWidth = 3 * fontSize;
	if(textWidth > length) textWidth = length;
	var lines 		= Math.round( length / textWidth);
//[c]  bottom middle
	if(botMidX ==null) botMidX 				= 0;
	if(botMidY ==null) botMidY 				= 0;
	if((botMidX ==0)&&(y ==0))
	{	if(w(id) != null)
		{	botMidX							= w(id).feet[0]; 
			botMidY 						= w(id).picXYZ[1]; 
		}	
	}

	
//[cf]
	
//[c]  top left
	var topX 					= botMidX -Math.round(textWidth /2);
	if(topX < 5) topX 			= 5;		// allow for book edge
	if(topX >(640 -textWidth)) topX =640 -textWidth;
	var topY 					= 0;
	var bigString 				= "";
//[c]
//[of]:message
if(id =="message")
{	GUIon		= 1;
	z 				= 3000002;		// image shadow is 3000000, then main image, then text
	var zShad 		= z -2;
	var zImg 		= z -1;
	var zClick 		= z +1;
	color			= "black";
	endClick();						// 
	
//[of]:x,y, etc
//[c]  width, height
	var fontHeight 	= Math.round(fontSize * 0.2);		// font size 100: letters around 20 pixels high
	var textHeight	= lines * fontHeight;
	var imgHeight	= textHeight + (fontHeight * 4);
	if(imgHeight < 100) imgHeight =100;
	var imgWidth 	= textWidth + (fontHeight * 4);
//[c]  x, y
	var imgY		= 235 - (imgHeight/2);
	var imgX		= 328 - (imgWidth/2);				// slightly right to allow for note shadow + book left
	topX 			= imgX + fontHeight;	
	topY 			= imgY + fontHeight +15;			// slightly lower to allow for page curl
	if(lines ==1)
	{ 	topX 		= topX +15; 						// to make it look right
	 	topY 		= 220;
	}	
//[cf]
	
//[c]  html (different divs because image is bigger than text, and shadow is transparent)
	var divShad		= makeDivString("message_shad",imgX,imgY,zShad,imgWidth,imgHeight);
	var imgShad 	= makeImgString(-1,"images/icons_etc/paper/message_s.gif",zShad,imgWidth,imgHeight,"");
	var divNote		= makeDivString("message",imgX,imgY,zImg,imgWidth,imgHeight);
	var imgNote 	= makeImgString(-1,"images/icons_etc/paper/message.gif",zImg,imgWidth,imgHeight,"");
	var divClick	= makeDivString("message",0,0,zClick,640,480);
	var imgClick 	= makeImgString(-1,"images/icons_etc/plain/clear.gif",zClick,640,480,"Click to close.");
	var textString	= simpleText(message,"",font,color,fontSizeAdd,textWidth,textHeight,topX,topY,z);
	bigString 		= divShad +imgShad +"</div>" + divNote +imgNote +"</div>" 
						+ textString +divClick +imgClick +"</div>";
	document.getElementById("SPspeechDiv").innerHTML =bigString; // **** THIS IS IT! ****	
	opacityNow("message_shad",30);		//100 = opaque
	return 1;
}
// to cancel, call 'stopTalkAndGUIs()' - this removes the entire contents of the <span>

//[cf]
//[c]  table allows easy positioning at bottom, while text space extends to top of screen
	var divT 	= "<div style=\"position: absolute; top:" +topY +"px; left: " +topX 
					+"px; width:" +textWidth +"px; height:" +botMidY +"px; "
					+"text-shadow: " +shadowColor +" 0em 0em 0.4em; z-index: " +z +"\">";
	var tableT 	= "<table style=\"width:" +textWidth +"px; height:" +botMidY 
					+"px;\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
					+"<tbody><tr><td style=\"vertical-align: bottom;\">";
	var fontT 	= "<h" +font +" style=\"font-size:" +fontSize +"%; color:'" +color +"'\"><strong>";
	var endT 	= "</strong></h" +font +"></td></tr></tbody></table></div>";
	bigString 	= divT +tableT +fontT +message +endT;
	document.getElementById("SPspeechDiv").innerHTML =bigString;	 // **** THIS IS IT! ****

	
//[c]  mouth
	talker 		= id;	
	w(id).targetXY =[0,0];					// forget where you were going
	w(id).checkCostume();					// force immediate stop + mouth animation
}


//[cf]
//[of]:stop talk +GUIs
function stopTalkAndGUIs()
{	if(document.getElementById("SPspeechDiv").innerHTML !="") 
		document.getElementById("SPspeechDiv").innerHTML ="";
//[c]  GUIs
	GUIon = 0;
	if(document.getElementById("SPmenu1").innerHTML !="")
		removeMenuItems();
	if(document.getElementById("SPGUIback").innerHTML !="")
		document.getElementById("SPGUIback").innerHTML ="";	
	if(document.getElementById("SPGUIcontent").innerHTML !="")
		document.getElementById("SPGUIcontent").innerHTML ="";	
//[c]  reset talk timer
	if(timeQueue[0] !=null)					// check the timer exists
	{	if(timeQueue[0].eType == "talk")	// just in case
			timeQueue[0].time = 0;
	}			
//[c]   normal folk's mouths
	for(var m =41; m < ww.length; m++)		// normal characters
	{	if(ww[m].room ==thisRoom)
		{	if(ww[m].id ==talker)
				return	stopTalker(m);
		}	
	}
//[c]  same for strangers
	if(ww[1].room !=thisRoom) return;		// any strangers?
	for(var n =0; n <41; n++)
	{	if(ww[n].room ==thisRoom)
		{	if(ww[n].id ==talker)
				return	stopTalker(n);
		}
	}			return stopTalker(0);		// just in case: e.g. 'GUIon'
}
//[c]
function stopTalker(num)
{	if(num)
	{	ww[num].mouthImg 	= ""; 
		ww[num].updateBothPics(0);			// exactly as last update, but without the mouth
	}
//[c]  reset talker
	talker 				= "";
	return 1;
}
//[cf]
//[c]  split and say
function say(id, string)//optional: font,color,fontSizeAdd,textWidth,x,y) // id =="message" = message box
{
//[of]:variables
//[c] friends
	if(id =="") id 		= starId;
	var id2 			= "";
	if(w(id) !=null)id1 = w(id).friend;
	if(id2 ==null) 	id2 = ""; 
	if(id2 !="") 
	{	w(id2).friend 	= id;
		walkToFriendof(id);	// relies on walk, which relies on targetXY. Blocked by 'w(id).restricted'
	}	
//[c]  optional variables (used as arguments to 'say1')
	var font	 	= arguments[2];if((font ==null)||(font ==0)) font = 1;
	var color  		= arguments[3];if((color ==null)||(color =="")) color = textColor[0];
	var fontSizeAdd	= arguments[4];if(fontSizeAdd ==null) fontSizeAdd = 0;
	var fontSize 	= defaultFontSize + fontSizeAdd;
	var textWidth 	= arguments[5]; if((textWidth ==null)||(textWidth ==0)) textWidth = 3 *fontSize; 
//[c]  only take x and y if forced: otherwise calculate character's position AT THE TIME
	var x 			= arguments[6]; if(x ==null) x = 0; 
	var y 			= arguments[7]; if(y ==null) y = 0;
//[c]  prepare
  	var string2 = ""; 
	var charac	= "";	
	var speaker = id; 
	if(string.substring(0,1)=="$")
	{	speaker = id2; 		// begins with the other person?
		color	= textColor[1];
	}
//[c] 
//[cf]
	
//[c]  split and say
	for(var i =0; i <= string.length; i++) 
  	{	if(i < string.length)
  			charac	= string.substring(i,i+1);			// one letter at a time
	 	if((charac =="@")||(charac=="$")||(i ==string.length)) 	// end of previous section? Or end of everything?
		{	if(string2 !="")							// tempString is built up with each loop
	  			say1(speaker,string2,font,color,fontSizeAdd,textWidth,x,y);
	    	string2 ="";								// reset for next section
	    	if(charac =="@") speaker = id;
	    	{	speaker = id; 		
				color	= textColor[0];
			} 
	    	if(charac =="$")
	    	{	speaker = id2; 		// begins with the other person?
				color	= textColor[1];
			} 
	    }
//[c]	  special case: replace star name
	    else 
	    {	if(charac =="^") charac =userName1;
	    	if(charac =="~") charac =userName2;
	  		string2 = string2 + charac;
	  	}	
   	}
}
function message(string)		// see 'start talk' for details
{	say("message",string);	
}
//[cf]
//[of]:turn
//[of]:decide direction
//[c]  r=0;   fr=45;   f=90;   fl=135;   l=180 (or -180);   bl=-135;   b =-90;   br =-45
function getangle(x1,y1,x2,y2)
{	var angle 	= Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
	return Math.round(angle);
}


function whereToTurn(id,x,y)
{	var num 	= w(id).who;					// for speed
	var angle 	= getangle(ww[num].feet[0],ww[num].feet[1],x,y);
	if(ww[num].howManyDirections ==2) 
	{	if((angle > 90)||(angle < -90))
			 return "l"; 
		return "r";
	}
	if(ww[num].howManyDirections ==4) 
	{	if((angle > 130)||(angle < -130))	// not a wider angle: use side animation where possible (looks best)
			return "l";
		if((angle > -50)&&(angle < 50))
			return "r";
		 if(y < w(id).feet[1])				// still here? Backwards or forwards then. Simple.
		 	return "b"; 
		 return "f";
	}						
	if(ww[num].howManyDirections ==8) 
	{	if(angle >22)
		{	if(angle < 68)return "fr";
 		  	if(angle <122)return "f";		//still here?
			if(angle <168)return "fl";
			return "l";
		}
		if(angle <-22)
		{	if(angle >-68)return "br";
			if(angle >-122)return "b";		//still here?
			if(angle >-168)return "bl"; 
			return "l";
		}return "r"; 
	}return "f"; 							// just in case
}
//[cf]
//[of]:queue part turns
function queuePartTurns(id,dir0)
{	if((id ==null)||(id =="")) id =starId;
	var nn  = w(id).who;				// for faster code: can use ww[nn]
	w(id).targetXYDir = dir0;
	var t1 	= new nE(); 				// t1 = turn1: up to 4 turns needed for any direction
	var t2 	= new nE();					// added AT START for immediate use, so be careful about the order!
	var t3 	= new nE(); 
	var t4 	= new nE();
//[c]   up to 4 turns
	var 					newDir	= getPartTurn(w(id).dir,dir0);
							t1.id =id; t1.dir =newDir; t1.time =4; t1.eType ="turnInst"; t1.x =1;
	if(newDir  != dir0)	{	newDir 	= getPartTurn(newDir, dir0);	// not got there yet?
							t2.id =id; t2.dir =newDir; t2.time =4; t2.eType ="turnInst"; t2.x =2;
						}
	if(newDir  != dir0)	{	newDir 	= getPartTurn(newDir, dir0);
							t3.id =id; t3.dir =newDir; t3.time =4; t3.eType ="turnInst"; t3.x =3;
						}
	if(newDir  != dir0)	{	newDir 	= getPartTurn(newDir, dir0);
							t4.id =id; t4.dir =newDir; t4.time =4; t4.eType ="turnInst"; t4.x =4;
						}
	
//[c]  called from timer queue? 
//[c]  Then 'turn' will be in the way, with time zero and ready to be deleted
//[c]  so place your part-turns AFTER that
	var sp =1;							// sp = splice = need to insert these AFTER the first event
	if(timeQueue ==null) sp =0;
	if(sp)	{	if(!timeQueue.length) sp =0;	
			}
	if(sp)	{	if((timeQueue[0].eType !="turnXY")&&(timeQueue[0].eType !="turn")) sp = 0;
			}
	if(sp)	{	if(timeQueue[0].time >1) sp =0;	// it has time to go? Must be something else - leave it.
			}									
//[c]  
//[c]add to the START
	if((t4.eType !="")&&(t4.dir !="")){if(sp)timeQueue.splice(1,0,t4); else timeQueue.unshift(t4);}
	if((t3.eType !="")&&(t3.dir !="")){if(sp)timeQueue.splice(1,0,t3); else timeQueue.unshift(t3);}
	if((t2.eType !="")&&(t2.dir !="")){if(sp)timeQueue.splice(1,0,t2); else timeQueue.unshift(t2);}
	if((t1.eType !="")&&(t1.dir !="")){if(sp)timeQueue.splice(1,0,t1); else timeQueue.unshift(t1);}
}
//[cf]
//[of]:decide next turn
function getPartTurn(from,to)				//returns the next compass point
{	if(from ==to)return from;
	var clockWise =["f","fl","l","bl","b","br","r","fr","f","fl","l","bl"];// a few extra to save looping
	var pos 	= 0; 
	for(var n =1; n < 8; n++)				// assume position1, try positions 2-7
	{	if(clockWise[n]==from)				// find 'from' position
		{	pos =n;
			break;							
		}
	}	
	var cw 		= 0;						// assume NOT a clockwise direction
	var np 		= pos +1; 
	if(clockWise[np] ==to) cw =1; 			// next clockwise position is target? Then move clockwise.
	np++; if(clockWise[np] ==to)cw =1; 		// or the next?
	np++; if(clockWise[np] ==to)cw =1; 		// or the next?
	if(cw)np 	= pos +1; 					// want next clockwise or anti-clockwise?
	else np 	= pos -1;					
	if(np <0)np = np +8;					// looped around?
	return clockWise[np];					// read from use the clock face we created earlier
}
//[cf]
//[of]:all turn
function allFaceExcept(x,y)
{	var id1 	= arguments[2];if(id1 ==null)id1 ="";		// people who do not turn
	var id2 	= arguments[3];if(id2 ==null)id2 ="";
	for(var n =41; n < ww.length; n++)
	{	if(ww[n].room ==thisRoom)
		{	if((w(n).id !=id1)&&(w(n).id !=id2))n.turnXY(x,y);
		}
	}
	if(ww[1].room ==thisRoom) 		// strangers as well?
	{	for(var m =0; m <41; m++)
		if(ww[m].room ==thisRoom)
		{	if((w(m) !=id1)&&(w(m) !=id2))m.turnXY(x,y);
		}
	}
}		

function allFaceRandom()
{	var id1 	= arguments[2];if(id1 ==null)id1 ="";		// people who do not turn
	var id2 	= arguments[3];if(id2 ==null)id2 ="";
	var x 		= Math.random()*800;
	var y 		= -300 + Math.random()*1200;
	allFaceExcept(x,y,id1,id2);
}
//[cf]

function turn(dir)
{	var id  	= arguments[1];if((id ==null)||(id =="")) id =starId;
	ww[sn].turn(dir,id);
}
function oTurn(direction)
{	var id  		= arguments[1];if((id ==null)||(id =="")) id =this.id;
	var myEvent 	= new nE(); 
	myEvent.eType 	= "turn";
	myEvent.id	= id;
	myEvent.dir 	= direction; // simply sets direction. 'checkCostume' calls 'queuePartTurns' at the time.
	push2(timeQueue, myEvent);
}
function oTurnXY(x,y)
{	var id  		= arguments[2];if((id ==null)||(id =="")) id =this.id;
	var myEvent 	= new nE(); 
	myEvent.eType 	="turnXY";
	myEvent.id	= id;
	myEvent.x		= x;
	myEvent.y		= y;
	push2(timeQueue, myEvent);
}
function oTurnInst(direction)
{	var id  		= arguments[1];if((id ==null)||(id =="")) id =this.id;
	var myEvent 	= new nE(); 
	myEvent.eType 	= "turnInst";
	myEvent.id	= id;
	myEvent.dir 	= direction; // simply sets direction. 'checkCostume' calls 'queuePartTurns' at the time.
	push2(timeQueue, myEvent);
}
function turnInstNow(id,direction)
{	if(this.restricted ==1)	  return 0;		// e.g. sitting down
	if((id ==null)||(id =="")) id = starId;
	if(w(id).dir ==direction) return 0;
	w(id).dir 	= direction;
	var loop 	= w(id).getLoop(w(id).wardrbs["myWard"].costumes["cos_idle"],4); // layer 4 = main image. getLoop automatically updates size.
	if(loop ==null) 		  return 0;
	var url 	= loop.baseURL +1 +".gif";
	w(id).img =url; 
	w(id).updateBothPics(0);
}
//[cf]
//[of]:animate

//[of]:simple animate
//[c]  play one loop then wait
function animate(id,loop)
{	var baseTime 	= arguments[2]; if(baseTime ==null)baseTime =2;
	var backwards 	= arguments[3]; if(backwards ==null)backwards =0;
	var start 		= arguments[4]; if(start ==null)start =0;
	var end 		= arguments[5]; if(end ==null)end =loop.numbers.length;// length > last position, but loop ends before that
	if(backwards) 
	{	for(var i =end -1; i >=start; i--)
		{	var time =baseTime; if(loop.delays[i]) time =time +loop.delays[i];
			var img =loop.baseURL + loop.numbers[i] +".gif";
			setFrame(id, img, time, loop.size[0],loop.size[1], loop.feetOffset[0], loop.feetOffset[1]);
		}
	}else
	{	for(var i =start; i < end; i++)	
		{	var time =baseTime; if(loop.delays[i]) time =time +loop.delays[i];
			var img =loop.baseURL + loop.numbers[i] +".gif";
			setFrame(id, img, time, loop.size[0],loop.size[1], loop.feetOffset[0], loop.feetOffset[1]);
		}	
	}	
}
//[cf]

//[of]:set frame
function setFrame(id,img)	
{ 	if(id =="") id =starNum;
	var time 		= arguments[2]; if(time ==null)time =2;
	var width 		= arguments[3]; if(width ==null)width =0;
	var height 		= arguments[4]; if(height ==null)height =0;
	var offsetX		= arguments[5]; if(offsetX ==null)offsetX =0;
	var offsetY		= arguments[6]; if(offsetY ==null)offsetY =0;
	var myEvent 	= new nE(); 	
	myEvent.eType 	= "frame"; 
	myEvent.id		= id;
	myEvent.time 	= time; 
	myEvent.img 	= img; 
	myEvent.x 		= width;
	myEvent.y 		= height;
	myEvent.offsetX	= offsetX;
	myEvent.offsetY	= offsetY;
	push2(timeQueue, myEvent);
}
function useFrameNow(id,img,width,height,offsetX,offsetY)
{	w(id).targetXY 	= [0,0];				// stops any walk
	w(id).img 		= img;
	w(id).size 		= [width,height];
	if(offsetX || offsetY)					// not just (0,0)?
	w(id).feetOffset = [offsetX,offsetY];
	w(id).updateBothPics(0);
	w(id).animating = 2; 					// overrides normal animation
}
//[cf]

//[c]
//[of]:sit/stand
function sit()
{	var id  = arguments[0];if((id ==null)||(id =="")) id =starId;
	if(id =="cmHero")
	{	turn("r");
		animate("cmHero",w("cmSaint").wardrbs["c00"].costumes["cos_sit"].r, 3);
		changeCostume("saintSit","r");
	}
}
function stand(id)
{	var id  = arguments[0];if((id ==null)||(id =="")) id =starId;
	if(id =="cmHero")
	{	animate("cmHero",w("cmSaint").wardrbs["c00"].costumes["cos_sit"].r, 3,1); // final 1 = backwards
		changeCostume("saintStand");
	}
}	
//[cf]

//[of]:bend
/*
function walkBend( who, x, y, stage)//1=start,2 =middle,3=all,4=just end
{ // prepare ----------------------------------------
  if((who <1)||((who +2) >CHARACTERS_TOTAL)) return 0;
	if(y ==0){x =character[who].x; y =character[who].y;}
  var downView =0; var wiggleView =0; var downTalkView =0;
  if((stage ==1)||(stage ==3)) // need to rememeber the start position?
  {	wbWho =who; // remembered outside the function for next time
  	wbView =character[who].NormalView;
  	wbTalkView =character[who].SpeechView;
  	wbIdlView =character[who].IdleView;
  }
  // characters ----------------------------------------
  if(bigScaleRoom(thisRoom))                                // ONLY BIG CHARACTERS BEND!
  { if(who ==ANGEL)   {downView =ANGEL_BEND; wiggleView =ANGEL_BEND2;downTalkView =ANGEL_BENTALK;}
    if(who ==LOVER_F){if((wbView ==LOVERF_D_SER_W)||(wbView ==LOVERF_D_W)){downView =LOVERF_D_BEND; wiggleView =LOVERF_D_BEND2;downTalkView =LOVERF_D_BTALK;}
                      if(wbView ==LOVERF_GREEK_W){ downView =LOVERF_GR_BEND; wiggleView =LOVERF_GR_BEN2;downTalkView =LOVERF_GR_BTLK;}
                      if(wbView ==LOVERF_HABIT_W){ downView =LOVERF_H_BEND; wiggleView =LOVERF_H_BEND2;downTalkView =LOVERF_H_BTALK;}}
    if(who ==PURE_F) {if((wbView ==PUREF_DRESS_W)||(wbView ==PUREF_DR_DIS_W)){downView =0; wiggleView =0;}
                      if((wbView ==PUREF_GREEK_W)||(wbView ==PUREF_GR_DIS_W)){downView =0; wiggleView =0;} }
    if(who ==EVIL_F) {if(wbView ==EVILF_DRESS_W){downView =0; wiggleView =0;}
                      if(wbView ==EVILF_ROBES_W){downView =0; wiggleView =0;}}
    if(who ==REBEL_F){if(wbView ==REBELF_DRESS_W){downView =0; wiggleView =0;}
                      if(wbView ==REBELF_OLD_W){downView =0; wiggleView =0;}
                      if(wbView ==REBELF_SIMP_W){downView =0; wiggleView =0;}}
    if(who ==REPORTER_F){if(wbView ==REPORT_CAS_W){downView =0; wiggleView =0;}
                      if(wbView ==REPORT_ELEG_W){downView =0; wiggleView =0;}
                      if(wbView ==REPORT_ROBES_W){downView =0; wiggleView =0;}}
    if(who ==GENERIC_F){if(wbView ==GEN_AUSTEN_W){downView =0; wiggleView =0;}
                      if(wbView ==GEN_CELTIC_W){downView =0; wiggleView =0;}}
    if(who ==SAINT_M) {downView =SAINT_BEND; wiggleView =SAINT_BEND2;downTalkView =SAINT_BENTALK;}
    if(who ==HERO_M) {if((wbView ==HERO_J_W)||(wbView ==HERO_J_SMILE_W)){downView =HERO_J_BEND; wiggleView =HERO_J_BEND2;downTalkView =HERO_J_BENTALK;}
                      if((wbView ==HERO_S_W)||(wbView ==HERO_S_SMILE_W)){downView =HERO_S_BEND; wiggleView =HERO_S_BEND2;downTalkView =HERO_S_BENTALK;}
                      if((wbView ==HERO_T_W)||(wbView ==HERO_T_SMILE_W)){downView =HERO_T_BEND; wiggleView =HERO_T_BEND2;downTalkView =HERO_T_BENTALK;}}
    if(who ==ROGUE_M){if(wbView ==ROGUE_J_W){downView =0; wiggleView =0;}
                      if(wbView ==ROGUE_S_W){downView =0; wiggleView =0;}
                      if(wbView ==ROGUE_T_W){downView =0; wiggleView =0;}}
    if(who ==SNAKE_M){if(wbView ==SNAKE_J_W){downView =0; wiggleView =0;}
                      if(wbView ==SNAKE_H_W){downView =0; wiggleView =0;}}
    if(who ==LOVER_M){if(wbView ==LOVERM_J_W){downView =LOVERM_J_BEND; wiggleView =LOVERM_J_BEND2;}
                      if((wbView ==LOVERM_S_W)||(wbView ==LOVERMS_S_W)){ downView =LOVERM_S_BEND; wiggleView =LOVERM_S_BEN2;}}
    if(who ==LOYAL_M){if(wbView ==LOYAL_J_W){downView =0; wiggleView =0;}
                      if(wbView ==LOYAL_S_W){downView =0; wiggleView =0;}}
    if(who ==PARENT_M){if(wbView ==PARENT_T_W){downView =0; wiggleView =0;}
                      if(wbView ==PARENT_G_W){downView =0; wiggleView =0;}}
    if(who ==ESTABLISHMENT_M){if(wbView ==ESTAB_J_W){downView =0; wiggleView =0;}
                      if(wbView ==ESTAB_R_W){downView =0; wiggleView =0;}}
    if(who ==EVIL_M){if(wbView ==EVILM_J_W){downView =0; wiggleView =0;}
                      if(wbView ==EVILM_P_W){downView =0; wiggleView =0;}}
    if(who ==POLICE_M){if(wbView ==POLICE_19_W){downView =0; wiggleView =0;}
                      if(wbView ==POLICE_R_W){downView =0; wiggleView =0;}}
    if(who ==YOUTH_M){if(wbView ==YOUTHM_S_W){downView =0; wiggleView =0;}
                      if(wbView ==YOUTHM_T_W){downView =0; wiggleView =0;}}
    if(who ==KING_OR_AMBASSADOR){if(wbView ==KING_H_W){downView =0; wiggleView =0;}
                      if(wbView ==KING_R_W){downView =0; wiggleView =0;}}
    if(who ==MENTOR_M){if(wbView ==MENTOR_J_W){downView =0; wiggleView =0;}
                      if(wbView ==MENTOR_R_W){downView =0; wiggleView =0;}}
    if(who ==RELIGIOUS_M){if((wbView ==RELIG_H_ANG_W)||(wbView ==RELIG_H_DIS_W)||(wbView ==RELIG_H_SMI_W)){downView =0; wiggleView =0;}}
    if(who ==BYSTANDER_M){if(wbView ==BYSTANDER_J_W){downView =0; wiggleView =0;}}
  }
  // just checking (from walk 2)? ----------------------------------------
  if(stage ==4)
  {	var v =character[who].View; if((v !=downView)&&(v !=wiggleView)&&(v !=downTalkView))return; }// not bent?
  // walk and bend ----------------------------------------
  if((stage ==1)||(stage ==3))
  { if(findDist(character[who].x,character[who].y, x,y) >10)
    {	if(itsOKtoWalk(who))
      { walk4(who, x, y);
        if(!isThisClose(who,x,y,8)) // not within 8 pixels of target?
          walk4(who, x, y, eBlock, eAnywhere);  // eAnywhere to force it
        wait2(10);}                                              // 'wait' = bugfix
    }
    character[who].FaceLocation(character[who].x,0,eBlock);	 		// face north
    character[who].Loop =3;
  	if(downView >0)
      animate2(who,downView,3,2,eOnce,eBlock,eForwards,true);		// bend down (freeze at end in case of delay)
    if((stage ==1)&&(downTalkView >0))
    { character[who].UnlockView();// because animate2 'true' meant lock at the end
      character[who].ChangeView(downTalkView);
      character[who].SpeechView = downTalkView;
      character[who].SetIdleView(-1, 0);
      wait2(10);       // 'wait' = bugfix
    }
    whoBending =who;
  }
  // middle part ------------------------------------------
  if((stage ==2)||(stage ==3))
  { if(wiggleView >0) animate2(who,wiggleView,3,3,eOnce,eBlock,eForwards,true);
		else wait2(60);
  }
  // end part ------------------------------------------
  if((stage ==3)||(stage ==4))
  { if(downView >0)
      animate2(who,downView,0,2,eOnce,eBlock,eBackwards,true);	// bend down (freeze at end in case of delay)
    if(wbWho ==who) 		// do not change if you don't have the right numbers!
    { character[who].Loop =3; // facing the right way, just in case
      character[who].ChangeView(wbView);
      character[who].SpeechView =wbTalkView;
      character[who].SetIdleView(wbIdlView, 1);wait2(10);        // 'wait' = bugfix
    }
    whoBending =0;
  }
}
*/
//[cf]
//[of]:hug
/*
function hug( stage)//1=start,2 =end,3=both
{	// for now, you must have lover_m (in sleeves) and lover_m (in dress)

	if(cLover_m.Room !=thisRoom) enterDefaultJustOffscreen(LOVER_M);
	if(cLover_f.Room !=thisRoom) enterDefaultJustOffscreen(LOVER_F);
	var howClose =78; // combined sprite has them 78 pixels apart
	var scaling = cLover_m.Scaling; howClose =howClose * scaling; howClose =howClose/100;
	var dir =2; // right
	if(cLover_m.x >cLover_f.x)dir =1; // lover_m needs to face left?
	if(dir ==1) howClose = 0 -howClose; // negative value: on left
	if((stage ==1)||(stage ==3))
	{	if(dir ==1)faceCompass(LOVER_M,"w"); else faceCompass(LOVER_M,"e");
		walk2(LOVER_F,cLover_m.x +howClose,cLover_m.y);
    wait2(45); // does this look right?
		cLover_m.ChangeView(LOVERS_EMBRACE);
		cLover_f.ChangeRoom(999);
		animate2(LOVER_M,LOVERS_EMBRACE,dir,3);
		cLover_m.ChangeView(LOVERS_EMBR_T);
		cLover_m.SpeechView =LOVERS_EMBR_T;
	}
	if(stage ==3) wait2(30);
	if((stage ==2)||(stage ==3))
	{	animate2(LOVER_M,LOVERS_EMBRACE,dir,3,eOnce,eBlock,eBackwards);
		setLook(LOVER_M,SLEEVES); // no longer two people in view
		cLover_f.ChangeRoom(thisRoom,cLover_m.x -howClose,cLover_m.y);
	}
}
*/
//[cf]
//[of]:loop background
//[c]  looping waves etc



/*

function addShip( room, x, y, withWave, view)
{	cShip.ChangeView(view);
	if(room <1) room =thisRoom;
	cShip.ChangeRoom(room,x,y);
	if(withWave)
	{	cShip_wave.ChangeRoom(room,x,y);
		cShip_wave.Transparency =20;
		cShip_wave.SetIdleView(cShip_wave.NormalView,0);
    cShip_wave.AnimationSpeed =-5; // fastest
		cShip_wave.FollowCharacter(cShip,FOLLOW_EXACTLY,0);//0 = in front
	}
}

*/
//[cf]
//[of]:explosions

/*
var boomSound; // sounds 49,50,51
function explosion( x, y, who)// see GOG game for more explosions
{	boomSound++;
	if((boomSound <49)||(boomSound >51))boomSound=49;
	PlaySound(boomSound);
	if(who <1) who =256 +Random(10);
	if(y <1) {x = mouse.x; y = mouse.y ;}
	if(who ==EXPLOSION1){y = y +20;} // compensate for explosions that start in the corner
	playFade(who,x,y,1,0,40);
}
*/
//[cf]
//[cf]
//[of]:walk
//[of]:start walk
function startWalk(id,x,y,anywhere)
{	if((id ==null)||(id =="")) id =starId;
	// restrict to the floor
	if(anywhere ==null) anywhere = 0;
	if(!anywhere)
	{	if(x <floorEdges[0])x 	= floorEdges[0];
		if(y <floorEdges[1])y 	= floorEdges[1];
		if(x >floorEdges[2])x 	= floorEdges[2];
		if(y >floorEdges[3])y 	= floorEdges[3]; 
	}
	w(id).targetXY[0] 		= x; 
	w(id).targetXY[1] 		= y;
	w(id).animating 		= 1;		// so code for position is changed, AND look for next frame
	w(id).timeToNextFrame 	= 0;
	stopTalkAndGUIs();						// only one person talks at a time, so stop 1 = stop all
	if(w(id).speed <1)w(id).speed =5; 	// speed = pixels per step if in a straight line
	w(id).targetXYDir 		= whereToTurn(id,x,y);
//[c]  turns are placed IN FRONT of the walk timer, which has NOT been deleted (because its time is not yet up)	
if(w(id).who >40)	queuePartTurns(id, w(id).targetXYDir);
	// when facing the right direction, 'checkCostume' handles the rest.
}
//[cf]
//[of]:walk speed
function oAdjustSpeed(speed)									// faster if tiny
{	if(this.scaleNow >0.99)return speed;
	speed =scaleIt(speed,this.scaleNow);
	if(this.scaleNow <0.15)return scaleIt(speed,2.7);	
	if(this.scaleNow <0.25)return scaleIt(speed,2.3);
	if(this.scaleNow <0.35)return scaleIt(speed,1.9);
	if(this.scaleNow <0.50)return scaleIt(speed,1.6);
	if(this.scaleNow <0.75)return scaleIt(speed,1.3); 
	return speed;												// don't forget the final return speed!!!!!
}
//[cf]
//[of]:stranger walk
//[c]  COMPLETELY SEPARATE from queued walk
function randomWalk(num)
{	if(ww[num].room !=thisRoom) return;
//[c]  already have a target?
	var gotTarget =0;
	if(ww[num].targetXY[0] !=ww[num].feet[0])gotTarget =1; 		// already trying to walk?
	else 
	{	if(ww[num].targetXY[1] !=ww[num].feet[1])gotTarget =1;
	}if(gotTarget)
	{	if((ww[num].targetXY[0] ==0)&&(ww[num].targetXY[1] ==0))
		gotTarget =0;// special case: (0,0) does not count
	}if(gotTarget)
		return;
//[c]  decide random x
	var anywhere =1;
	var x = -50; 
	if(floorEdges[0] >10)
	{	x =floorEdges[0];
		anywhere =0;
	}
	if(ww[num].feet[0] <320)// already too close to that side?
	{	x =690; 
		if(floorEdges[2] <630)
		{	x =floorEdges[2];
			anywhere =0;
		}
	}
//[c]  decide random y
	var floorHeight =floorEdges[3] -floorEdges[1];
	var y= floorEdges[1] + Math.floor(Math.random()*floorHeight);
	startWalk(ww[num].id,x,y,anywhere);	
}
//[cf]
//[c]  'stop walk' is by 'stopEverything'
function walk(id,x,y)
{	var anywhere  =arguments[3];if(anywhere ==null)anywhere =0; // default is floors, NOT anywhere
	if(id =="")id =starId; 
	ww[sn].walk(x,y,anywhere,id);// uses story sn timer: interrupts all others
}

function oWalk(x,y)// sets target
{ 	var anywhere 	= arguments[2]; if(anywhere ==null)anywhere =0; // default: NOT anywhere
	var id	 		= arguments[3]; if((id ==null)||(id =="")) id =this.id;
//[c] still here? Fill in the variables then.
	var myEvent 	= new nE(); 	
	myEvent.eType 	= "walk"; 		
	myEvent.id		= id;		
	myEvent.restrictions = anywhere;
	myEvent.time 	= 10;		// constantly reset; stopped by 'checkCostume'
	myEvent.x 		= x; 
	myEvent.y 		= y;
	push2(timeQueue, myEvent);
}

function walkNear(id,x,y)
{	if(w(id) ==null) id = starId;
	if((x ==w(id).feet[0])&&(y ==w(id).feet[1]) )return;		// not walking to yourself?
	var close 		= Math.round(w(id).scaleNow);
	if(x < w(id).feet[0])close = 0 -close;
	w(id).walk(x +close, y);
	w(id).turnXY(x,y);
}
function walkToFriendof(your_id)              		
{	var friend 		= w(your_id).friend;
	if(friend =="")return -1;
	if(friend ==your_id) return -1;
	if(w(friend).room !=thisRoom)return -1;
	if(friend =="")return;
	walkNear(your_id,w(friend).feet[0],w(friend).feet[1]);
}
//[cf]
//[of]:fade
//[of]:events
function opacityNow(id,amount)//100 = opaque
{	var element = document.getElementById(id);
	if(element !=null)
 	{  element.style.opacity =amount/100; //most browsers
	   element.style.filter = "alpha(opacity = "+amount+")"; //IE
	}  
}
function fadeEvent(id,opac,delay)
{	if(opac <0)   opac 	= 0; 
	if(opac >100) opac 	= 100; 
	var myEvent 		= new nE(); 	
	myEvent.eType 		= "opacity"; 		
	myEvent.id		= id;	
	myEvent.opac 		= opac;				
	myEvent.time 		= delay; 
	push2(timeQueue, myEvent);
}
function addWindow(img,opac)
{	var myEvent 		= new nE(); 
	myEvent.eType 		= "window";
	myEvent.img			= img;
	myEvent.opac 		= opac;	
	myEvent.time 		= 0;
	push2(timeQueue, myEvent);
}

function addBack(img)
{	var myEvent 		= new nE(); 
	myEvent.eType 		= "back";
	myEvent.img	 		= img;
	myEvent.time 		= 0;
	push2(timeQueue, myEvent);
}
//[cf]
function fadeInOut(id,wantIn)// uses sn's timer
{	var time = arguments[2]; if(time ==null) time =framesPerSec;
	var opacPerFrame = Math.round(100/time);
	var delay =1;
	if(opacPerFrame <1)					// more than 100 frames?
	{	opacPerFrame =1; 
		delay =2;						// keep it simple: long fades take 200 frames. Done.
	}	
	if(wantIn)
	{	for(var opac=0; opac <100; opac = opac +opacPerFrame)
			fadeEvent(id,opac,delay);
	}else	
	{	for(var opac=100; opac >0; opac = opac -opacPerFrame)
			fadeEvent(id,opac,delay);
	}
}
//[c] fade to black = fadeWindow("images/icons_etc/plain/black.gif")
//[c] fade to white = fadeWindow("images/icons_etc/plain/white.gif")
function fadeWindowIn(url) // uses "hotspotWindow"
{	addWindow(url,0);
	fadeInOut("hotspotWindow",1);
}
//[c]
function fadeWindowOut() // uses "hotspotWindow"
{	fadeInOut("hotspotWindow",0);
	addWindow("images/icons_etc/plain/clear.gif");
}
//[c]
function fadeToBlackMessage(message, stage)// stage: 0=all,1 =start, 2 =stop
{ 	if((stage ==0)||(stage ==1)) // start
	{	fadeWindow("images/icons_etc/plain/black.gif");
    	if(message =="")wait(60);
    	if(message !="")say("message",message);
    	// TO DO:
    	//	special case: "message" is invisible, centered (text is auto centered anyway), large font, white.
  	}
  	if((stage ==0)||(stage ==2))// end
		fadeWindowOut();
}
//[cf]
//[of]:puzzles


//[c]  special puzzles:
//[of]:make music (cave)
/*									// e.g. 15030 = note 15, hold for 30;		2003	= note 2, hold for 3
var caveTimer; 						// for rep_ex_always timing
var caveNoteWait;
var nextNote;
var caveNotesPlayed[15]; 			// can remember up to 14 values
var caveNotesWanted[15];			// e.g. ( 0.0.0.0.0.0.0.0.0.0.1.2.3.4)
var latestTune;
function startCaveNote( n)
{//	if(n ==1) {music7low.NormalGraphic 		=	music7low.PushedGraphic; 	 PlaySound(42);	}
}
function endCaveNote()
{//	music7low.NormalGraphic 	=1558;
}
function playCaveNote()
{	var note = 0;												// silent
	if(nextNote <14) note = caveNotesWanted[nextNote];
	var delay =6;												// default
	if(note >1000)											// e.g. 5003
	{ delay = note/500; 								// e.g. 10
		note = note -(delay *500);				// e.g. 5003 -(10 * 500)
	}
	endCaveNote();
	startCaveNote(note);
	caveNoteWait =delay;
	nextNote++;
	caveTimer =0;
	if(nextNote ==14)
	{	caveTimer =0; 				// ready to remove last glow
		caveNoteWait =25;	}
	if(nextNote >14)
	{	nextNote = 0;				// 'caveNotesWanted' is reset to a new tune whenever you play it right
		caveTimer =-500; 			// don't play again for a while, unless the user clicks
		caveNoteWait =0;	}		// ready to play immediately, when timer hits zero
}
function rep_ex_caveplay() 			// called by rep_ex_always whenever caveTimer >0
{	if(nextNote <0)return;
	if(caveTimer >=caveNoteWait)
	{	endCaveNote();
		playCaveNote(); 			// also resets 'caveTimer' and sets 'caveNoteWait'
	}
}
function playlatestTune() 			// called when entering cave, or when caveTomer gets back to
{	caveTimer =0; 					// rep_ex will play as soon as this hits 'caveNoteWait'
	caveNoteWait =0;				// will play now!
	nextNote =0;					// the note to play
}
//-------------- ABOVE: play. BELOW: prepare, check -------------------------
function checkIfPlayedRight()
{		var i =13; var noteWanted; var notePlayed;
		var temp=0;
		while(i >=0)
		{	noteWanted = caveNotesWanted[i];
			notePlayed = caveNotesPlayed[i];
			if(noteWanted <1) return 1;															// got to the end? Must all be correct!
			if(noteWanted >1000){temp = noteWanted /1000;	temp =temp * 1000; noteWanted =noteWanted -temp;}	// forget about timing
			if(notePlayed >1000){temp = notePlayed /1000;	temp =temp * 1000; notePlayed =notePlayed -temp;}
			if(notePlayed != noteWanted) return 0; 									// that note does not match?
			i--;																												// still here? look at previous note
		} return 1; 																								// still here? Must all be correct!
}
function findSpaceAtEnd()						 	// 'rememberCaveNote' fills up to position 13
{		var howMuchSpaceAtEnd =0;
		var position =13;
		while(position >=0)
		{	if(caveNotesWanted[position]<1)	// that space is empty?
				howMuchSpaceAtEnd++;
			position--;
		}return howMuchSpaceAtEnd;
}
function moveMusicToEnd()							// 'rememberCaveNote' fills up to position 13
{	var howFarToMove = findSpaceAtEnd();
	var to =13; 												// start at the end (since it's most likely to be empty)
	var from = 13 - howFarToMove;				// look back to first full space from end
	while(from >=0)											// keep going until you get back to the start
	{	caveNotesWanted[to] = caveNotesWanted[from];	// move it up to the end
		to--; from--; 										// move the nest one to the enxt-to-end, and so on
	}
}
function wantCaveMusic( n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13)
{	caveNotesWanted[0] =n0; 		caveNotesWanted[1] =n1;			caveNotesWanted[2] =n2;
	caveNotesWanted[3] =n3;			caveNotesWanted[4] =n4;			caveNotesWanted[5] =n5;
	caveNotesWanted[6] =n6;			caveNotesWanted[7] =n7;			caveNotesWanted[8] =n8;
	caveNotesWanted[9] =n9;			caveNotesWanted[10] =n10;		caveNotesWanted[11] =n11;
	caveNotesWanted[12] =n12;		caveNotesWanted[13] =n13;
	moveMusicToEnd();
}
function setLatestTune()
{	var i =0; while (i <=13){caveNotesWanted[i] =-1; 	i++;	} // reset (so 'moveMusicToEnd' can spot empty spaces)
	if(latestTune ==0) wantCaveMusic(20003,15004,15005,15006,0		,0		,0		,0		,0		,0		,0		,0		,0		,0		); // each whole 1000 means a delay of 1 frame
	if(latestTune ==1) wantCaveMusic(20001,12002,12003,12004,12005,12006,12007,12008,12009,12010,12011,12012,12013,12014);
											latestTune =0;
}
var passedMusicTest; // global flag, because can't trigger cutscene from here (not defined yet)
function rememberCaveNote( note)		// puts latest note in position 30 and shunt others down by one
{		var new =0; var old =1;
		while(old <=13)
		{ caveNotesPlayed[new] = caveNotesPlayed[old]; new++; old++; } // push everything down by 1
		caveNotesPlayed[13] = note;
		if(checkIfPlayedRight())					// see if the pattern of notes played fits the pattern of wanted notes
		{		say2(ANGEL,"Yes, that's the tune I heard!");
				latestTune++; setLatestTune();
//				if(!chapterDone(PLAY_MUSIC))passedMusicTest =1;
		}
}


// music GUI -------------------------------------------------------------------------------------

function checkMusic(sound, note)
{	caveTimer =-120;	// delay before background music can continue
	PlaySound(sound);
	rememberCaveNote(note);
	if(passedMusicTest >0)
	{	if(eventsDone(SEE_EQUATION,SEE_DRAWINGS))
		{	if(!eventDone(PLAY_MUSIC))
			{	chapterToEnd =PLAY_MUSIC; cutscene(PLAY_MUSIC); }
		}
	}
}
#sectionstart music7low_Click  // DO NOT EDIT OR REMOVE THIS LINE
function music7low_Click(GUIControl *control, MouseButton button) {
	checkMusic(42,1);
}
*/
//[cf]
//[of]:semaphore
/*
function moveViewClockwise( view)
{ if(view ==0)return 6; if(view ==6)return 1;; if(view ==1)return 7; if(view ==7)return 3;
  if(view ==3)return 5; if(view ==5)return 2; if(view ==2)return 4; if(view ==4)return 0;;
}
var semLetter;
function semaphoreLetter( v1, v2)
{ var d1 ="down"; semLetter =""; // default
  if(v1 ==1)d1 ="left"; if(v1 ==2)d1 ="right"; if(v1 ==3)d1 ="up"; if(v1 ==4)d1 ="down-right";
  if(v1 ==5)d1 ="up-right"; if(v1 ==6)d1 ="down-left"; if(v1 ==7)d1 ="up-left";
  var d2 ="down";
  if(v2 ==1)d2 ="left"; if(v2 ==2)d2 ="right"; if(v2 ==3)d2 ="up"; if(v2 ==4)d2 ="down-right";
  if(v2 ==5)d2 ="up-right"; if(v2 ==6)d2 ="down-left"; if(v2 ==7)d2 ="up-left";
  if((d1 =="down-left")&&(d2 =="down")    )  {semLetter ="a"; return;}
  if((d1 =="left")     &&(d2 =="down")    )  {semLetter ="b"; return;}
  if((d1 =="up-left")  &&(d2 =="down")    )  {semLetter ="c"; return;}
  if((d1 =="up")       &&(d2 =="down")    )  {semLetter ="d"; return;}
  if((d1 =="down")     &&(d2 =="up-right"))  {semLetter ="e"; return;}
  if((d1 =="down")     &&(d2 =="right")   )  {semLetter ="f"; return;}
  if((d1 =="down")     &&(d2 =="down-right")){semLetter ="g"; return;}
  if((d1 =="down-left")&&(d2 =="left")    )  {semLetter ="h"; return;}
  if((d1 =="down-left")&&(d2 =="up-left") )  {semLetter ="i"; return;}
  if((d1 =="up")       &&(d2 =="right")   )  {semLetter ="j"; return;}
  if((d1 =="down-left")&&(d2 =="up")      )  {semLetter ="k"; return;}
  if((d1 =="down-left")&&(d2 =="up-right"))  {semLetter ="l"; return;}
  if((d1 =="down-left")&&(d2 =="right")   )  {semLetter ="m"; return;}
  if((d1 =="down-left")&&(d2 =="down-right")){semLetter ="n"; return;}
  if((d1 =="left")     &&(d2 =="up-left") )  {semLetter ="o"; return;}
  if((d1 =="left")     &&(d2 =="up")      )  {semLetter ="p"; return;}
  if((d1 =="left")     &&(d2 =="up-right"))  {semLetter ="q"; return;}
  if((d1 =="left")     &&(d2 =="right")   )  {semLetter ="r"; return;}
  if((d1 =="left")     &&(d2 =="down-right")){semLetter ="s"; return;}
  if((d1 =="up-left")  &&(d2 =="up")      )  {semLetter ="t"; return;}
  if((d1 =="up-left")  &&(d2 =="up-right"))  {semLetter ="u"; return;}
  if((d1 =="up")       &&(d2 =="down-right")){semLetter ="v"; return;}
  if((d1 =="up-right") &&(d2 =="right")   )  {semLetter ="w"; return;}
  if((d1 =="up-right") &&(d2 =="down-right")){semLetter ="x"; return;}
  if((d1 =="up-left")  &&(d2 =="right")   )  {semLetter ="y"; return;}
  if((d1 =="right")    &&(d2 =="down-right")){semLetter ="z"; return;}
}
var semaphoreMessage[20];
var saidSemLength =new Boolean(0);
var semPos; // semaphore letter position
function updateSemMessage( letter, update)
{ var n =0; if(update ==0) // reset?
            { while(n <20)
              { semaphoreMessage[n] =""; n++;}
                semPos =0; return;
            }
  if(semPos >=20) // make space by shifting everything down one
  { semPos =0; n =1; // lose the first position and replace it with the next one
    while(n <20)
    { semaphoreMessage[semPos] =semaphoreMessage[n];
      semPos++;
      n++;
    } semPos =19;
  }
  var toSay ="";                                              // ready to respond
  if(letter =="") // replaces full string
  { n = Random(5);if(n ==0)toSay ="That's not right.";
    if(n ==1)toSay ="I don't know what letter that is.";
    if(n ==2)toSay ="Semaphore is harder than it looks.";
   }else
  { semaphoreMessage[semPos] =letter; semPos++;                 // add the letter
    toSay ="'"; n =0;                                           // make the word
    while(n <20)
    { if(semaphoreMessage[n] !="")toSay = toSay.Append(semaphoreMessage[n]); //ready to sa
      n++;
    }toSay = toSay.Append("'");
  }  
  sayOrThink(storyStar, toSay);
  // respond to letters ------------------------------------------------------
  if(toSay.Length ==6)//"'1234'" includes dashes
	{ var comment =new Boolean(0);
	  if(toSay =="'sprv'"){if(chapterDone(BUY_SPANISH_BONDS)&& !chapterDone(FALSE_TELEGRAPH_SIGNAL))
	                          return 1;} // still here? Then carry on
	var country =toSay.Substring(1,2);
	var theEvent =toSay.Substring(3,2);
	var sayCountry ="";
	if(country =="au")sayCountry ="in Austria";	if(country =="br")sayCountry ="in Britain";	
	if((theEvent !="")&&(country !=""))
  { toSay =var.Format("I think that means %s %s. %s",sayEvent,sayCountry,sayEventResponse); comment =1}
	if(toSay =="'love'"){sayOrThink(storyStar, "Love is all you need."); comment =1}
	// default response ------------------------------------------------------
	if(!comment) toSay ="Messages can only have four letters. I'll start again.";
	sayOrThink(storyStar,toSay); comment =1 
	n =0; while(n <20) { semaphoreMessage[n] =""; n++;} semPos =0; // reset
	
  } return 0;


}
function changeSemaphore( which) // 0 =reset, 1 =left, 2 =right, 3=done
{ if(which ==0){updateSemMessage("",0); return;}
  if(which ==1){PlaySound(75); var n =moveViewClockwise(cSemaphore.Loop); cSemaphore.Loop =n; return;}
  if(which ==2){PlaySound(75); var n =moveViewClockwise(cSemaphore2.Loop);cSemaphore2.Loop =n; return;}
  // still here? Must be 'which ==3' then.
  semaphoreLetter(cSemaphore.Loop,cSemaphore2.Loop); // updates semLetter
  var success =new Boolean(updateSemMessage(semLetter, 1));
  PlaySound(75);
  cSemaphore.Loop =0;
  cSemaphore2.Loop =0;
  return success;
}// semaphore code - that triggers 'chapterToEnd' and a cutscene - is in 'look at' code
*/


//[cf]
//[of]:chemist shop

/*
// special case for chemist shop ---------------------------------------------
function namePrep(GUIControl* label, what, which) // e.g. 'arrow1' corresponds with hotspot '1'
{	if(which ==1){whatPrep =var.Format("preparation %s",what);
            label.AsLabel.Text =var.Format("Preparation %s: patent antidote[   (for common poisons)",what); 
  }else    {whatPrep2 =var.Format("preparation %s",what);
            if(whatPrep2 ==whatPrep){whatPrep2 =""; return false;}
            label.AsLabel.Text =var.Format("Preparation %s:  arrhythmius[   (for faking death)",what); 
            }return true;
}
function choosePreparation( which)// choose 2 preparations
{	var i =Random(25);// 0-25 =26 choices
	if(i ==0)return namePrep(book26A,"A",which);  if(i ==1)return namePrep(book26B,"B",which);
	if(i ==2)return namePrep(book26C,"C",which);  if(i ==3)return namePrep(book26D,"D",which);
	if(i ==4)return namePrep(book26E,"E",which);  if(i ==5)return namePrep(book26F,"F",which);
	if(i ==6)return namePrep(book26G,"G",which);  if(i ==7)return namePrep(book26H,"H",which);
	if(i ==8)return namePrep(book26I,"I",which);  if(i ==9)return namePrep(book26J,"J",which);
	if(i ==10)return namePrep(book26K,"K",which); if(i ==11)return namePrep(book26L,"L",which);
	if(i ==12)return namePrep(book26M,"M",which); if(i ==13)return namePrep(book26N,"N",which);
	if(i ==14)return namePrep(book26O,"O",which); if(i ==15)return namePrep(book26P,"P",which);
	if(i ==16)return namePrep(book26Q,"Q",which); if(i ==17)return namePrep(book26R,"R",which);
	if(i ==18)return namePrep(book26S,"S",which); if(i ==19)return namePrep(book26T,"T",which);
	if(i ==20)return namePrep(book26U,"U",which); if(i ==21)return namePrep(book26V,"V",which);
	if(i ==22)return namePrep(book26W,"W",which); if(i ==23)return namePrep(book26X,"X",which);
	if(i ==24)return namePrep(book26Y,"Y",which); if(i ==25)return namePrep(book26Z,"Z",which);
}


function prepareStory()
{	whatPrep =""; whatPrep2 =""; var xFrz =0;
  choosePreparation(1);
  while(whatPrep2 ==""){choosePreparation(2); // keep trying until you get something different
                        xFrz++; if(xFrz >100){Display("bug message 1");
                        whatPrep2 =whatPrep;}} // highly unlikely, but I don't like 'while' loops

*/
//[cf]

//[c]  avoid hitting things

//[c]  avoid falling off coach

//[c]  arrange shape




//[c]  Flash puzzles:	1. default: remember something. Variation: link many things (e.g. candelabra)
//[c] 				2. make a shape: angels.
//[c] 				3. shoot. variation: object continually shoots, you changes its direction. Variation: destroyed object spawns.
//[c] 				4. move objects into position (levers, etc)	variation 1: Heath Robinson. variation 2: nudge something - e.g. whirlwind
//[c] 				5. Pacman: move and 'eat' stuff while someone chases you
//[c] 				6. Moving back=ground. e.g. coach race avoid rocks; bubbles - avoid hitting. variation: shoot at the same time.
//[cf]
//[c]  story
//[of]:CHAPTERS
//[c]  chapter object: name, 

//[of]:done? during?
function chapterDone(num)
{	if(num <0) 						return 1;
	if(num >=chapters.length)		return 0;
	if(chapters[num].state ==3)		return 1; 
	return 0;
}
//[c]
function chaptersDone(start, end)
{	if(end < start){var s =start; start =end; end =s;} // just in case
	if(start <0) start =0;
	for(var n =start; n <=end; n++)
	{if(!chapterDone(n)) return 0; }		// are any of these events NOT done?
	return 1;								// still here? They must all be done then!
}					
//[c]
function betweenChapters(start, end) 		// 1 of these is open, AND all previous chapters done
{	if(end < start){var s =start; start =end; end =s;} // just in case
	if(!chaptersDone(0, start-1))	return 0;
	if(!chaptersDone(start, end))	return 1;
}
//[c]
function duringChapter(num) 
{	if(!chaptersDone(0,num-1))		return 0;
	if(!chapterDone(num))			return 1;
	return 0;
}
//[c]
function findFirstOpenChapter()	 //
{	for(var n =0; n < chapters.length; n++)
	{if(!chapterDone(n)) return n; }
	return -1;								// must be all done then
}
//[c]
function showOpenEvents()
{	var ev ="Currently live events:\n";
	for(var i =0; i < chapters.length; i++)
	{	if(chapters[i].state ==2)	ev = ev +chapters[i].name+"\n";
	}
	ev = ev +"awaiting actors:\n";
	for(var j =0; j < chapters.length; j++)
	{	if(chapters[j].state ==1)	ev = ev +chapters[j].name+"\n";
	}
	allert(ev);
}
//[cf]
//[of]:triggers

function triggered(string)
{	allert("checking if '"+string+"' is a trigger");
	for(var i =0; i < chapters.length; i++)	
	{	if(chapters[i].state ==2)
		{	for(var j =0; j < chapters[i].triggers.length; j++)	
			{	if(chapters[j].triggers ==string)return i;
				if(chapters[j].triggers =="anything")return i;
				if(chapters[j].triggers =="anyone")
				{	if(w(string) !=null)
					{	if(w(string).personType >0)return i;
					}				
				}
			}	
		}
	} return 0;
}	
//[cf]
//[of]:add actors

function unlockChapters(nameJustDone)						// checks all possible chapters
{	for(var i =0; i < chapters.length; i++)	
	{	if(doesItDependOnYou(chapters[i].name, nameJustDone))
			refreshThisChapter(chapters[i].name)	
	}
}
function refreshThisChapter(it)	
{	if(gotWhatItDependsOn(it))
	{	if(tryToAddActorsFor(it))
			 ch(it).state =2;								// added everyone, ready to trigger?
		else ch(it).state =1;								// will add some when you leave this room
	}
}

function doesItDependOnYou(it,you)	
{	var obj = ch(it);
	for(var i =0; i < obj.dependsOn.length; i++)	
	{	if(obj.dependsOn[i] ==you)return 1; 
	}return 0;
}
function gotWhatItDependsOn(it)
{	var obj = ch(it);
	for(var i =0; i < obj.dependsOn.length; i++)	
	{	if(ch( obj.dependsOn[i] ).state !=3)return 0; 
	}return 1;
}
function tryToAddActorsFor(it)
{	var allAdded =1;
	var obj = ch(it);
	for(var i =0; i < obj.needs.length; i++)	
	{	if(obj.needs[i].room ==thisRoom)			// can't just appear here - looks wrong
			allAdded =0;
		else
		{	if(ww(obj.needs[i].id) ==thisRoom)		// can't just disappear from here - looks wrong
				allAdded =0;
			else
				add(obj.needs[i].id, obj.needs[i].room, obj.needs[i].x, obj.needs[i].y);
		}
	}return allAdded;										// a single error would set it as false
}
//[cf]
//[of]:cutscenes

function endEvent()								// called by last cutscene //
{	ch(chapterToEnd).state =3;				
	unlockChapters(chapterToEnd);				// any more chapters now possible?
 	var num =ch(chapterToEnd).num;
  	num++;
  	if(num <= chapters.length) 
  		refreshThisChapter(chapters[num].name);	// may have been unlocked long ago and objects moved
	sponCount =0;
	chapterToEnd ="";
}

function checkHere(id,room,x,y,toSay)			// was 'notThere' - simpler now, no problems with room changes
{	if(room !=thisRoom)
	{	sayOrThink("",toSay, 2); 			   	// e.g. 'I found the clue'
		walk(id,-50,450); 						// just in case (ignored if not there)
		changeRoom(where);
	}	
	if(w(id).room !=room)
	{	add(id);								// we have to change rooms? Just place them there
		walk(id,x,y);
	}
}

//[cf]

//[of]:jump to chapter

function removeChapterPeople()								// removes anyone added by 'needs'
{	for(var i =0; i < chapters.length; i++)					// step through every chapter
	{	for(var j =0; j < chapters[i].needs.length; j++)	// step through every need
		{	var name = chapters[i].needs[j].name;			// find that person
			w(name).room =-1;								// set his room 	
		}
	}	
}
//[c]  yes, it's quicker to just remove everyone, but there might be people moved by the user though they may not be saved)


function jumpToChapter(name)	
{	removePlotPointPeople(); 							// removes anyone added by 'needs'
	var chapNum =ch(name).num;
	if(chapNum >=chapters.length) chapNum =chapters.length -1;
	for(var i =0; i < chapNum; i++)			
	{	chapters[i].state =1; 
		tryToAddActorsFor(chapters[i].name);
		chapters[i].state =3; 	eState[i]=3;
	}
//[c]  next, update all FUTURE events
	for(var j =chapNum; j < chapters.length; j++)			
	{	chapters[j]=0; 																	// assume it is untouched
		refreshThisChapter(chapters[j].name);
	}
}
//[cf]

//[c] chapters() array (see variables) is filled by 'story' module
function cO() 								// cO = chapter object
{	this.num 			= 0;				// set when creating story	
	this.name 			= ""; 	
	this.state			= 0;				// 0=not ready, 1=ready for actors, 2=ready to run, 3=done
	this.dependsOn		= new Array();		// chapter names
	this.needs 			= new Array();		// mO obects: includes room, id, etc.
	this.triggers		= new Array();		// strings
	this.f1clues		= new Array();		// of strings
	this.otherClue		= new Array();		// of 'clue' objects
}	
//[c]  pushing and popping is much easier than keeping track!
function clue()
{	this.say			= "";
	this.id				= "";				// could be an object or hotspot as well
	this.type 			= 0;				// for hotspot room or other variable
}

//[c]  find chapter by name (like w(id)
function ch(name) 
{	{	for(var i =0; i < chapters.length; i++)		// find others
		{	if(chapter[i].name ==name)return chapters[i];
		}
	}return null;
}
//[cf]
//[c]  playing the game
//[of]:MOUSE CLICKS ---
//[of]:click objects
function clickObject(who)						// number, not id, for easy fit with html
{	if(!startClick())	return 0;		
//[of]:menu objects
//[c]  menu items stored in 'people//ob: NOT people, but identified by number for convenience
	if(who >=2900000)
	{	stopTalkAndGUIs();
		if((who >3000010)&&(who <3000019))		// 3000011-18 = menu items 1-8
		{	switch(who)
			{	case 3000011: showArrows();		break;	// first time: also shows message about the game
				case 3000012: giveHint();		break;
				case 3000013: showMap();		break;
				case 3000014: showHistory();	break;
				case 3000015: showSaveOpen();	break;
				case 3000016: showTools();		break;
				case 3000017: showCopyright();	break;
				case 3000018: showBook();		break;
			}	
		}
		return endClick();	
	}
//[c]  end of GUI section
//[cf]

	if((who >0)&&(who <100)) return runTestCode("hello");
	
	if(numIsPerson(who))
		checkClick(ww(who).id, mouseX, mouseY);	// includes 'end click'
	else	endClick();	
}
//[c]
function clickHotspot(id) 		
{	if(!startClick())	return 0;
	checkClick(id, mouseX, mouseY);				// includes 'endClick'
}
//[c]
function clickMapLabel(room) 		
{	if(!startClick())	return 0;
	changeRoom(room);	
	endClick();
}
//[c]
function clickBackground()
{	if(!startClick())	return 0;
	if(stopAllWalks())							// if somebody was walking, a click just stops them.
	{	endClick(); 
		return 0; 
	}		
	if(document.getElementById("SPspeechDiv").innerHTML !="") 
	{	stopTalkAndGUIs();
		return endClick();	
	}
	if(timeQueue[0] ==null)						// no other events queued up?
		ww[sn].walk(mouseX, mouseY, 0);			// 1 = anywhere
	return endClick();
}
//[cf]
//[of]:click X or H
function clickX()					// includes 'allowClick' because click calls three times
{	if(!allowClick)		return 0;	// don't allow multiple responses in same click 
	hitEsc();						// includes endClick
}	
function clickH()					// includes 'allowClick' because click calls three times
{	if(!allowClick)		return 0;	// don't allow multiple responses in same click 
	mainMenu();						// includes endClick
}	
//[cf]
//[of]:mouseover
if (!IE) document.captureEvents(Event.MOUSEMOVE);
document.onmousemove = getMouseXY;
//[c]
function getMouseXY(e)									// e = any event
{	if (IE)
	{	mouseX = event.clientX + document.body.scrollLeft;
		mouseY = event.clientY + document.body.scrollTop;
	}else
	{	mouseX = e.pageX;
		mouseY = e.pageY;
	}
//[c]  position
	if( mouseX < 0 )		mouseX 	= 0; 		// not too close to the edge
	else { if(mouseX >640)	mouseX	= 640;	}
	if( mouseY< 0 )			mouseY 	= 0; 
	else { if(mouseY >480) 	mouseY	= 480;	}
//[c]  text
	var mouseTextX 		= mouseX - 20;
	var mouseTextY		= mouseY - 50;		// if too close, mouse touches mouseText
	if( mouseTextX < 0 )		mouseTextX		= 0; 
	else { if((mouseTextX >640)&&(mouseTextX <740)) 
								mouseTextX 		= 640; 	}
	if( mouseTextY < 0 )		mouseTextY		= 0; 
	else { if((mouseTextY >480)&&(mouseTextY <580))
								mouseTextY 		= 480; 	}
//[c]  do it
	document.getElementById("mouseOver").style.left =mouseTextX + "px";
	document.getElementById("mouseOver").style.top  =mouseTextY + "px";
	mouseStill =0;										// ready to hide mouseover on mobile phones etc.
	return true;
}
//[c]
function dMouseText(text)								// mouseOver
{	document.getElementById("mouseOver").innerHTML="<h6>"+text+"</h6>";
}
//[c]
//[cf]
//[of]:CHECK CLICK
//[of]:clues
function readClues(id)
{	allert("checking if '"+id+"' triggers a clue");
	var chap = findFirstOpenChapter();
	if(chap <0) return 0;
	for(var i =0; i < chapters[chap].otherClue.length; i++) 
	{	if(chapters[chap].otherClue[i].id ==id)
		{	say("",chapters[chap].otherClue[i].say);
			return 1;
		}
	}return 0;
}
//[cf]
//[of]:exits
function checkExit(name)
{	allert("Checking whether '" +name +"' is an exit in room '" +thisRoom +"'");
}
//[cf]
//[of]:talk to
function talkTo(id)
{	allert("in same room, so trying to talk to '"+id+"'");
	return 0;
}
//[cf]
//[of]:think about
function thinkAbout(id)
{	allert("thinking about '"+id+"'");
	return 0;
}
//[cf]

//[c]
function checkClick(id, x, y)
{	if(document.getElementById("SPspeechDiv").innerHTML !="") // just stop talking
	{	stopTalkAndGUIs();	
		return endClick();
	}
	stopEverything(id);

	allert("1. asefs dgsdbkawe navnvk");
	allert("2. eg ev ee  e easefsdgsdbkawenavnvk");
	allert("3. d rgeg  rt rst  rth asef sdgsdb kawenavnvk");
	
	// remember object name for 'recent clicks'
	
	if(triggered(id))											// triggers defined in chapter section
		return endClick();
	walkNear(id,x,y);
	if(ww[sn].room ==thisRoom)									// clues
	{	if(readClues(id))
		 return endClick();
	}
	if(checkExit(id))											// exits
		return endClick();
	if(id ==starId)												// sponTalk defined in 'every so often' section
	{	if(trySpon())
		 return endClick();
	}
	if(ww[sn].room ==thisRoom)									// talk to
	{	if(talkTo(id))
		 return endClick();
	}  
	if(thinkAbout(id))											// think about
		 return endClick();
	return 0;
}
//[c]
//[cf]
//[of]:scroll wheel
//[cf]
//[of]:keyboard
function keyboard(e)
{	if(GUIon)		{	stopTalkAndGUIs();	return endClick();	}
	var evtobj	= window.event? event : e //distinguish between IE's explicit event object (window.event) and Firefox's implicit.
	var unicode=evtobj.charCode? evtobj.charCode : evtobj.keyCode
	var letter=String.fromCharCode(unicode);
	var keycode;
	if (window.event) keycode = window.event.keyCode;	// bug fix for IE as usual
	else if (e) keycode = e.which;

//	allert("you just pressed '" +keycode);	
	if(keycode ==32)	spaceBar();						// includes 'endClick'
	if(keycode ==13)	hitEnter();	
	if(keycode ==27)	hitEsc();	
	if((letter =="x")||(letter =="X"))	hitEsc();
	if((letter =="h")||(letter =="H"))	mainMenu();
		
}
//[of]:all key codes
//[c]UPPER AND LOWER CASE SAME:
//[c]	This detects low level keyboard input. Upper and lower are decided at a higher level,  depending on the state of the shift key.
//[c]F1:	The number esists ibut is usually not passed to the browser. F5 also refreshes - so avoid those keys.
//[c]Instead?
//[c]1234567890 - favorite rooms?	h - help; other letters - other GUI shortcuts?
//[c]
//[c]backspace	8
//[c]tab			9
//[c]enter		13
//[c]shift		16
//[c]ctrl			17
//[c]alt			18
//[c]pause/break	19
//[c]caps lock	20
//[c]escape		27
//[c]page up		33
//[c]page down	34
//[c]end			35
//[c]home		36
//[c]left arrow	37
//[c]up arrow	38
//[c]right arrow	39
//[c]down arrow	40
//[c]insert		45
//[c]delete		46
//[c]0	48
//[c]1	49
//[c]2	50
//[c]3	51
//[c]4	52
//[c]5	53
//[c]6	54
//[c]7	55
//[c]8	56
//[c]9	57
//[c]a	65
//[c]b	66
//[c]c	67
//[c]d	68
//[c]e	69
//[c]f	70
//[c]g	71
//[c]h	72
//[c]i	73
//[c]j	74
//[c]k	75
//[c]l	76
//[c]m	77
//[c]n	78
//[c]o	79
//[c]p	80
//[c]q	81
//[c]r	82
//[c]s	83
//[c]t	84
//[c]u	85
//[c]v	86
//[c]w	87
//[c]x	88
//[c]y	89
//[c]z	90
//[c]left window key		91
//[c]right window key	92
//[c]select key	93
//[c]numpad 0	96
//[c]numpad 1	97
//[c]numpad 2	98
//[c]numpad 3	99
//[c]numpad 4	100
//[c]numpad 5	101
//[c]numpad 6	102
//[c]numpad 7	103
//[c]numpad 8	104
//[c]numpad 9	105
//[c]multiply	106
//[c]add			107
//[c]subtract	109
//[c]decimal point	110
//[c]divide	111
//[c]f1	112
//[c]f2	113
//[c]f3	114
//[c]f4	115
//[c]f5	116
//[c]f6	117
//[c]f7	118
//[c]f8	119
//[c]f9	120
//[c]f10	121
//[c]f11	122
//[c]f12	123
//[c]num lock	144
//[c]scroll lock	145
//[c]semi-colon	186
//[c]equal sign	187
//[c]comma		188
//[c]dash		189
//[c]period		190
//[c]forward slash	191
//[c]grave accent	192
//[c]open bracket	219
//[c]back slash		220
//[c]close braket	221
//[c]single quote	222
//[c]
//[cf]
document.onkeydown=keyboard;	// onkeydown woeks on all browsers; onkeypress does not (e.g. for escape)
//[c]
function spaceBar()
{	allert("you pressed the space bar");
	return endClick();
}
function hitEnter()
{	stopTalkAndGUIs();
	return endClick();
}
function hitEsc()
{	stopTalkAndGUIs();	
	return endClick();
}
function mainMenu()
{	stopTalkAndGUIs();	
	addMenuItems();
	return endClick();
}
//[cf]

//[c]
function startClick()					// only allow one click at a time
{	if(!allowClick)		return 0;		// don't allow multiple responses in same click 
	if(GUIon)							// message box prevents all other clicks?
	{ 	stopTalkAndGUIs();
		endClick();						// allow them to click again soon
		return 0;						// but go no further
	}
	if(block)
	{	endClick();						// allow them to click again soon
		return 0;						// but go no further
	}	
	allowClick =0;		return 1;		// stop for next time ('endClick' resets this)
										// ...(IE responds to hotspot AND background on same click)
}


function endClick()
{	allowClick 	= 0;
	setTimeout("clickAllowed()",333)	// wait 1/3 second before allowing another click
	return 1;							// so parent function can call "return endClick"
}
function clickAllowed()
{	allowClick	= 1;
}
//[c]

//[cf]
//[of]:GUIs
//[c] message box: 		see   talk ->start talk
//[c] stopTalkAndGUIs: 	see   talk
//[of]:main menu
//[of]:menu back
function menuBack(title)							// typical title scale = 1.4, 2.0, etc.
{	var titleScale = arguments[1]; 		
	if((titleScale ==null)||(titleScale < 0.2))
		titleScale =1.8; 							// default size
	var fontSizeAdd = Math.round((titleScale *100) -100);
//[c]  image
	var divString 	= makeDivString("GUIback",0,0,2900000);
	var imgString 	= makeImgString(2900000,"images/icons_etc/paper/paper.jpg",2900000,640,480,"");
//[c]  title
	var titleString = simpleText(title, "", "", "", fontSizeAdd, 640,50,0,0,2900001);
	var bigString 	= divString + imgString +titleString +"</div>";
	fillSpan("SPGUIback", bigString);
}
//[cf]

function addMenuItems()// = character numbers 3000000 + number
{	addMenuItem(1,"info");
	addMenuItem(2,"hint");
	addMenuItem(3,"map"); 
	addMenuItem(4,"history"); 
	addMenuItem(5,"save,open"); 
	addMenuItem(6,"tools"); 
	addMenuItem(7,"copyright"); 
	addMenuItem(8,"book"); 
}	
function removeMenuItems()	
{	for(var i =1; i <=8; i++)
		fillSpan("SPmenu" +i, "");	
}
function addMenuItem(num,text) 			
{	var x 			= 494;				// position 1: info
	var y 			= 11;
	var width		= 148;
	var height		= 50;
	switch(num)			
	{	case 2: x =495;	y =71;	width = 149;	height = 50;	break;	// 2: hint
		case 3: x =483;	y =130;	width = 161;	height = 47;	break;	// 3: map
		case 4: x =482; y =186; width = 165;	height = 51;	break;	// 4: history
		case 5: x =482; y =246; width = 160;	height = 51;	break;	// 5: save and open
		case 6: x =486;	y =305;	width = 159;	height = 52;	break;	// 6: tools
		case 7: x =487; y =365; width = 157;	height = 52;	break;	// 7: copyright
		case 8: x =485; y =424;	width = 159;	height = 47;	break;	// 8: book
	}
	var img 		= "images/icons_etc/menus/bookmark" +num +".gif";
	var id 			= "menu" +num;
	var divString 	= makeDivString(id,x,y,3000010);
//[c]  for link purposes, treat as character 3000010 +num
	var imgString 	= makeImgString(3000010 +num,img,3000010,width,height,text)
	id 				= "SP" + id;
	var bigString 	= divString + imgString +"</div>";
	fillSpan(id, bigString);
}

//[cf]
//[c]
//[of]:info
//[c]  first time: about the game; tell them next time to look in the tools menu
//[c]  thereafter: exits + 		CHAPTER NUMBER  (for navigating without cookies)
//[of]:arrows

/*
//-------------------------------------------------------------------------------------------
var personLabel;
var personLabelYleft;	// so that edge names don't overlap
var personLabelYright;
//------------------ ARROWS ----------------------------------------------------------
var exitList[17];
var exitGrid[52];                                    // 5 x 10 grid: each slot 128 x 48
function stopArrowOverlapping(GUIControl* arrow)
{	if(arrow.AsButton.Visible ==false) return 0;
	var x = (arrow.AsButton.X +64)/128;       // screen is 5x128 wide. If over half way along, round up.
  if(x >4)x=4;
	var y = (arrow.AsButton.Y +24)/48;      // screen is 10x48 high. If over half way down, round down.
	if(y >9)y=9;
	var gridNum =(y *5) +x;			            // 0-4 on first line, 5-9 on second line, etc.
	var done =new Boolean(0);
	// try nearby positions on the grid
	var tryGrid =gridNum;
	if(exitGrid[tryGrid] ==0)done =1	  // that space is empty?
  // left and right?
	if((x>0)&&(x<4))                        // edge arrows don't go sideways
  { if(!done){tryGrid =gridNum-1;if(exitGrid[tryGrid] ==0)done =1} // try space to the left
    if(!done){tryGrid =gridNum+1;if(exitGrid[tryGrid] ==0)done =1} // try space to the right
	}// above and below?
	var i =1; while(!done)                  // try 1 to 9 lines above and below
	{ if(y >=i)															// try i lines above
		{	tryGrid =gridNum -(5 *i);           // current position 15, -10, -15, etc.
      if(exitGrid[tryGrid] ==0)done =1// -5 for each line
	  }if(!done &&(y <=(5 -i)))							// try i lines below
	  {	tryGrid =gridNum +(5 *i);
      if(exitGrid[tryGrid] ==0)done =1// +5 for each line
    }i++;
    if(i >9)done =1                   // cannot be more than 9 lines away from original
	}
	exitGrid[tryGrid] =1;
  y =tryGrid/5; x =tryGrid -(y*5);        // e.g. 34 becomes (4,6)
	arrow.AsButton.X =(x *128)+20; //+20 = bugfix: or they appear too far left
	arrow.AsButton.Y =y *48;
  // check text width
  var width = GetTextWidth(arrow.AsButton.Text,1); if(width <100) width = 100;
  var excess = (width -94)/2 + 13;		    // how far text sticks out each side
  if(arrow.X < 30)					    arrow.X = excess -5;
  if(arrow.X >660)              arrow.X = 640 -102 -excess;
}
function showArrowButton(GUIControl* which, num) // e.g. 'arrow1' corresponds with hotspot '1'
{	which.AsButton.Visible =0
  //-------------------------------------------- is it an exit? -----------------------------------------------
	var hotspotNum =exitList[num];
  if(hotspotNum <1) return 0;
  // names and positions ----------------------------------------------------------------------
  which.AsButton.Text = hotspot[hotspotNum].Name;
  if(checkLocationAlias(which.AsButton.Text))	which.AsButton.Text = locationAlias;
  if(which.AsButton.Text !="") which.AsButton.Visible =1
  var x =hotspot[hotspotNum].WalkToX;
  var y =hotspot[hotspotNum].WalkToY;
  // special cases: don't point to that place ----------------------------------------------------------------------
  if(thisRoom ==9){ if(which.AsButton.Text =="telegraph"){x =477;y =176;}
                       if((which.AsButton.Text =="Paris")||(which.AsButton.Text =="Paris outskirts"))
                                                            {x =1609;y =208;}}
  if(thisRoom ==14){if(which.AsButton.Text =="Paris outskirts"){x =50;y =282;} }
  if(thisRoom ==18){if(which.AsButton.Text =="Paris streets"){x =216;y =248;}}
  if(thisRoom ==26){if(which.AsButton.Text =="French countryside"){x =111;y =207;}
                       if(which.AsButton.Text =="Paris road"){x =270;y =225;} }
  if(thisRoom ==28){if(which.AsButton.Text =="chateau"){x =200;y =200;}}



  //-------------------------------------------------------------------------------------------------
  which.X = x -40;		    // guis are positioned at top left
  which.Y = y -20;
  // images and colors
    var width = GetTextWidth(which.AsButton.Text,1); if(width <100) width = 100;
  var excess = (width -94)/2 + 13;		    // how far text sticks out each side
  // images and colors
  which.AsButton.TextColor = speechColor;           which.AsButton.NormalGraphic =81;
  if(which.Y < 16)							 which.Y = 16;
  if(which.Y >460)							{which.Y = 410;			which.AsButton.NormalGraphic =3991;}
  if(which.X < 20)					    {which.X = excess;	which.AsButton.NormalGraphic =3992;}
  if(which.X < width)					   which.X = excess;
  if(which.X >660)              {which.X = 640 -94 -excess; which.AsButton.NormalGraphic =3993;}
  if(which.X >(640 -94 -width))  which.X = 640 -94 -excess;
  // dark?
  var luminance = getColorLuminance(game.text_shadow_color); 														// how light is this room?
  if(luminance <40)
  {	if(which.AsButton.NormalGraphic ==81) 	which.AsButton.NormalGraphic =3994;					// dark versions of arrows
    if(which.AsButton.NormalGraphic ==3991) which.AsButton.NormalGraphic =3995;
    if(which.AsButton.NormalGraphic ==3992) which.AsButton.NormalGraphic =4198;
    if(which.AsButton.NormalGraphic ==3993) which.AsButton.NormalGraphic =4197;
  }
  which.AsButton.PushedGraphic =which.AsButton.NormalGraphic;
  which.AsButton.MouseOverGraphic =which.AsButton.NormalGraphic;
  stopArrowOverlapping(which);
}
function showArrowsGui() // space bar
{	if(gArrows_gui.Visible) return;										// already visible?
	if(gSave.Visible) return;
	if(gCredits_gui.Visible) return;
	if(gMap_gui.Visible) return;
	// stop walkers()
	var i =0; while(i <40){i++; character[i].StopMoving(); }
	timer_walkers =90;                                // starts counting down to zero
	// unhide and outline storyStar
	gui_circle.Visible = 0
 	if(character[storyStar].Room ==thisRoom)
 	{	gui_circle.X 					= character[storyStar].x -46;
		gui_circle.Y 					= character[storyStar].y -46 -20;
		if(bigScaleRoom(thisRoom))	gui_circle.Y =gui_circle.Y -150; // don't just circle their feet!
		else													gui_circle.Y =gui_circle.Y -35; // don't just circle their feet!
		gui_circle.Text 			= GetTranslation(character[storyStar].Name);
		gui_circle.Visible 		= 1
	}
	// oval room label
	getRoomName(thisRoom);
	var location = roomName;
	location = GetTranslation(location);
	location = var.Format("here: %s", location);
	gui_arrow_location.Text = location;
	if(getColorLuminance(game.text_shadow_color) <40)	// dark room?
	{	gui_arrow_location.TextColor 		 =65503;  	gui_arrow_location.NormalGraphic =3518; }
	else
	{	gui_arrow_location.NormalGraphic =4199; 		gui_arrow_location.TextColor 		 =4096;	}
  // find all exits
  i =0; while(i<16){exitList[i] =0; i++;}           // clear list
  i =1;                                             // cycle through again, filling with exits
  var n =1; var hotspotType =0;
  while(n<50){ hotspotType =hotspot[n].GetProperty("type");
               if(isExit(hotspotType))
               {  exitList[i] =n; i++; if(i ==16)n =50; }// escape loop, just in case
               n++;
             }
 // add arrows
  i =0; while(i<51){exitGrid[i] =0; i++;}           // clear grid
  exitGrid[31]=1;exitGrid[32]=1;exitGrid[33]=1;     // oval room name fills these spaces
  exitGrid[46]=1;exitGrid[47]=1;exitGrid[48]=1;     // chunky arrows don't fit on this line
	showArrowButton(gui_arrow_1,1);		showArrowButton(gui_arrow_2,2);		showArrowButton(gui_arrow_3,3);
	showArrowButton(gui_arrow_4,4);		showArrowButton(gui_arrow_5,5);		showArrowButton(gui_arrow_6,6);
	showArrowButton(gui_arrow_7,7);		showArrowButton(gui_arrow_8,8);		showArrowButton(gui_arrow_9,9);
	showArrowButton(gui_arrow_10,10);	showArrowButton(gui_arrow_11,11);	showArrowButton(gui_arrow_12,12);
	showArrowButton(gui_arrow_13,13);	showArrowButton(gui_arrow_14,14);	showArrowButton(gui_arrow_15,15);
	showArrowButton(gui_circle,0);
	gArrows_gui.Visible =1
}
*/
//[cf]

function showArrows()
{	message("info:\n first time: 'about the game'\n thereafter: arrows.\n'about game' can be accessed from tools menu. \n arrow code will be added after we have some exits ready!");

//	fillSpan("SPGUIcontent", bigString);
}

//[cf]
//[of]:hint
	
function giveHint()
{	message("info:\n first time: message about hint system, then hint.'\n thereafter: just the hint.");
}
	
//[cf]
//[of]:map
//[c] WHEN MAP IS MADE: 	fill in	room.mapXY  for every room that you want on the map.
//[c] 						 negative numbers mean "show always"
function showMap()
{	menuBack("map");
	var z =	2900010;									// 2900000,2900001 = GUIback and title
	var bigString			= "";						// ready to add map labels
	for(var i =0; i < rooms.length; i++)
	{	if((rooms[i].mapXY[1] <0)||(rooms[i].visited))	// negative Y means ALWAYS show
		{	var x =rooms[i].mapXY[0]; 	if(x <0) x = 0 -x;
			var y =rooms[i].mapXY[1]; 	if(y <0) y = 0 -y;
			if(x ||y)
				bigString = bigString + addMaplabel(i, x, y, z);
		}
	fillSpan("SPGUIcontent", bigString);
	}
}
function addMaplabel(room,x,y,z)
{	var width 		= 140;			// table, centreed on x
	x 				= x -(width /2);
	if( x < 0)   x 	= 0;
	if( x >(640 -width)) x 	= 640 -width;
	var textLink 	= "clickMapLabel(" +room +")";
	return simpleText(rooms[room].name,textLink,"","",0,0,0,x,y,z);
}
//[cf]
//[of]:history
//[c]  FIND OUT: can javascript auto-open a drop down list ? For keyboard shortcuts?
//[c]  		or just go to the history page - not much of a problem really.

function showHistory()
{	menuBack("history");
//[c]  recent rooms

//[c]  recent dialog

//[c]  recent music

//[c]  recent clicks


//	fillSpan("SPGUIcontent", bigString);
}

function setSaveDialog(id, what)				  	// for 'gDialog' GUI
{	replaceAll(what, "\n"," ");						// replace line breaks with spaces
  	what =w(id).name +": "+what;
  	saveDialog.unshift(what);						// add result to start
	if(saveDialogNow.length >100)saveDialog.pop(); 	// only save last 100 lines
}



/*
//---------------------------------------------- SET memory
function setMemoryMusic( music) 
{ if(music ==66)return; // silent track
  var n =0; while (n <=highestSlotUsed)
	{ if(memoryMusic[n] ==music)return; n++;}	// make sure it's not there already
	memoryMusic[memoryMusicNow] =music;
  highestSlotUsed++;  // starts at -1 (set in GlobalScript), first use sets to zero
  if(highestSlotUsed >84)highestSlotUsed =84; // max
	memoryMusicNow++;
	if(memoryMusicNow >84) memoryMusicNow = 0;// loop and start overwriting
}
function setMemoryWhat( what, whatWho, whatWhere)
{	// set it as currently clicked object
	rightClickedObject =what;
  if(what ==""){if((whatWho >0)&&(whatWho <CHARACTERS_TOTAL))
                      rightClickedObject =character[whatWho].Name;}
	rcObjectPerson =whatWho;
	rcObjectRoom =thisRoom;
	// add to memory list if not already there
	var n =0; while (n <10)
	{ if(memoryWhat[n] !=null){ if(memoryWhat[n] ==what)return;} // make sure it's not there already
    n++;
  }memoryWhat[memoryWhatNow] = what;
	memoryWhatWho[memoryWhatNow] = whatWho;
	memoryWhatRoom[memoryWhatNow] = whatWhere;
	memoryWhatNow++; if(memoryWhatNow >9) memoryWhatNow = 0;
	cursorColor("Xwhite");																// fakes a right click
	lastClick =2; 																				// pretend you just right clicked
}
var memoryArrowsClicked =new Boolean(0);
function showMemoryWheres()// it scrolls!!
{	var maxm =30;
	// count total ---------------------------------------------------------
	var totalUsed =0; var i =0;
  while(i <maxm)
	{	if((memoryRoom[i] >1)&&(memoryRoom[i] <81))totalUsed++;
		else {memoryRoom[i] =0; memoryRoomName[i] ="";}	// avoid crashes
		i++;
	}// where do we start showing? -----------------------------------------
	if(!memoryArrowsClicked) memoryRoomShown =memoryRoomNow;
	var startFrom =memoryRoomShown -7;
	if(startFrom <0)startFrom =maxm +1 +startFrom; 				// e.g. 0,-1,-2 becomes 0,30,29
	if(totalUsed <9)startFrom =0;												// no need for scroll buttons?
	// don't show blanks at start ------------------------------------------
	i =0; while (i <5)// only try a few
	{ if(memoryRoom[startFrom] <2)startFrom--; if(startFrom <0) startFrom =maxm;
    i++;}
	// show arrow buttons? -------------------------------------------------
	var previous =startFrom -1; if(previous <0) previous =maxm;
	if(memoryRoom[previous] >1)whereUp.Visible =1 else whereUp.Visible =0
	var next =startFrom +8; if(next >maxm)next =0;
	if(memoryRoom[next] >1)whereDown.Visible =1 else whereDown.Visible =0
	// now fill the buttons! -----------------------------------------------
	var mm = startFrom;	 	if(memoryRoomName[mm] !=null)memory21.Text =memoryRoomName[mm];
	mm++;if(mm >maxm)mm =0; if(memoryRoomName[mm] !=null)memory22.Text =memoryRoomName[mm];
	mm++;if(mm >maxm)mm =0; if(memoryRoomName[mm] !=null)memory23.Text =memoryRoomName[mm];
	mm++;if(mm >maxm)mm =0; if(memoryRoomName[mm] !=null)memory24.Text =memoryRoomName[mm];
	mm++;if(mm >maxm)mm =0; if(memoryRoomName[mm] !=null)memory25.Text =memoryRoomName[mm];
	mm++;if(mm >maxm)mm =0; if(memoryRoomName[mm] !=null)memory26.Text =memoryRoomName[mm];
	mm++;if(mm >maxm)mm =0; if(memoryRoomName[mm] !=null)memory27.Text =memoryRoomName[mm];
	mm++;if(mm >maxm)mm =0; if(memoryRoomName[mm] !=null)memory28.Text =memoryRoomName[mm];
	// don't forget favorite rooms -----------------------------------------------
  if(favRoom[1] >0) favorite1.Text =favRoomName[1];
  if(favRoom[2] >0) favorite2.Text =favRoomName[2];
  if(favRoom[3] >0) favorite3.Text =favRoomName[3];
  if(favRoom[4] >0) favorite4.Text =favRoomName[4];
  if(favRoom[5] >0) favorite5.Text =favRoomName[5];
  if(favRoom[6] >0) favorite6.Text =favRoomName[6];
}
function scrollMemoryWhere( change)
{	memoryArrowsClicked =1
	memoryRoomShown =memoryRoomShown +change;
	if(memoryRoomShown <0)memoryRoomShown =30;
	if(memoryRoomShown >30)memoryRoomShown =0;
	showMemoryWheres();
}
function setMemoryWhere()
{ var where = thisRoom;
	// make sure it's not already in the list
	var n =0; while (n <=30)
	{ if(memoryRoom[n] ==where)return; 	// make sure it's not already in the list
		n++;
	}
	memoryRoom[memoryRoomNow] = where;
	getRoomName(where);	memoryRoomName[memoryRoomNow] =roomName;
	memoryRoomNow++; if(memoryRoomNow >30) memoryRoomNow = 0;
}
//-------------------------------------------------------------
var dialogText;
function updateDialogLabel( line)
{	var text ="";
	if((line >=0)&&(line <101))// not past the end of the list
	{ if(dialogText.Length <1600)// just in case: GUI labels have max 2000 characters and 35 lines
    { if(saveDialog[line] !=null)
      dialogText =var.Format("%s[%s", dialogText, saveDialog[line]);
    }
	}
}
function showMemoryDialog()
{ var size =13;
  var line = memoryDialogBot -size;					// start 'size' lines before current line
	dialogText ="";                           // 'memoryDialogBot' = last line added
	updateDialogLabel(line);
  var i =0; while (i<size){ line++;updateDialogLabel(line); // adds 'size' recent lines
                          i++;}
	dialog0.Text =dialogText;
	dialog0.Text =dialogText;
	if(memoryDialogBot >(size -2))dialogUp.Visible =1 else dialogUp.Visible =0
	if(memoryDialogBot <memoryDialogNow)dialogDown.Visible =1 else dialogDown.Visible =0
}

//---------------------------------------------- USE memory
function getMemoryMusic( plusWhat)		    				// plusWhat is the button +1 or -1
{	var nowPlaying =GetCurrentMusic();								// don't use 'memoryMusicNow' (it doesn't change while credit GUI is on)
	var position =0;
	// find current music in list
  var foundit =new Boolean(0);
	var i =0; while(!foundit && (i <=highestSlotUsed))
	{	if(memoryMusic[i] ==nowPlaying)
		{ position = i; foundit =1} 	        		  // escape loop
		i++;
	}if(!foundit)position =highestSlotUsed;           // couldn't find it?
  var nextPos = position +plusWhat;
  if(nextPos <0) nextPos =highestSlotUsed;          // looped round?
  if(nextPos >highestSlotUsed) nextPos =0;          // looped round?
	return memoryMusic[nextPos];					            // found something? Success.
}
function getaWhereMemory( rName)									// here right clicking == left clicking
{	var roomNumber =rm(rName);
  gMap_gui.Visible =0 // so it isn't left up if you have to save the game (for moving games)
  gRecent.Visible =0
	if(roomNumber <1) return;															// avoid accidentally going to room 0!
	changeRoom2(roomNumber);
	// If NON-follower right clicks on a map location, they do NOT see the name
	// 		 (because to make it work I'd need a major re-write of the map code)
	// 				(because right click code is in a LATER module)
}
function getaWhatMemory( button)											// note: the argument is always 1-10
{	cursorColor("Xwhite");																// fakes a right click
	PlaySound(13); 																				// low click
	lastClick =2; 																				// pretend you just right clicked
	//------- find the object ----------------------------------------------------------------
	var memoryWhatWanted = memoryWhatNow -button;				 	// e.g. button1 shows memoryWhat -1, button2 shows memoryWhat -2, etc.
	if(memoryWhatWanted <0) memoryWhatWanted = memoryWhatWanted +10;
	rightClickedObject = memoryWhat[memoryWhatWanted];// remember what you clicked
	if(rightClickedObject =="") return;
	rcObjectPerson = memoryWhatWho[memoryWhatWanted];	// and their character number (if any)
	if((rcObjectPerson >0)&&(rcObjectPerson <CHARACTERS_TOTAL))
  { if(character[rcObjectPerson].GetProperty("type") > -1)
        setFriends(storyStar, rcObjectPerson);  }
	rcObjectRoom   = memoryWhatRoom[memoryWhatWanted];
	gRecent.Visible =0
	//------- DUPLICATES SOME RIGHT CLICK CODE ------------------------------------------------------------------
					lastClick =2; 														// pretend you just right clicked
					if(character[storyStar].Room !=thisRoom)	removeStrangers();				// bug fix: avoid crashes
					changeRoom2(character[storyStar].Room, character[storyStar].x, character[storyStar].y);
							// call 'changeRoom' EVEN IF THEY ARE IN THIS ROOM
							// so because 'checkStoryOnRightClick' is triggered
							// (defined in a later module, so cannot simply call that function)
}


	
//---------------------------------------------- SHOW memory
function showMemories()
{ if(gRecent.Visible) { closeEveryGui(); return; }
	closeEveryGui();		// close previous GUIs
	var mm = memoryWhatNow -1; 		// most recent addition
		if(mm <0)mm =9; if(memoryWhat[mm] !=null) memory11.Text =memoryWhat[mm];
  mm--; if(mm <0)mm =9; if(memoryWhat[mm] !=null) memory12.Text =memoryWhat[mm];
  mm--; if(mm <0)mm =9; if(memoryWhat[mm] !=null) memory13.Text =memoryWhat[mm];
  mm--; if(mm <0)mm =9; if(memoryWhat[mm] !=null) memory14.Text =memoryWhat[mm];
  mm--; if(mm <0)mm =9; if(memoryWhat[mm] !=null) memory15.Text =memoryWhat[mm];
  mm--; if(mm <0)mm =9; if(memoryWhat[mm] !=null) memory16.Text =memoryWhat[mm];
  mm--; if(mm <0)mm =9; if(memoryWhat[mm] !=null) memory17.Text =memoryWhat[mm];
  mm--; if(mm <0)mm =9; if(memoryWhat[mm] !=null) memory18.Text =memoryWhat[mm];
  mm--; if(mm <0)mm =9; if(memoryWhat[mm] !=null) memory19.Text =memoryWhat[mm];
  mm--; if(mm <0)mm =9; if(memoryWhat[mm] !=null) memory20.Text =memoryWhat[mm];
  gRecent.Visible =1
}




function pressFavorite(which, theButton)
{ if(favRoom[which] <1)Display("Hold down the mouse and press %s/nto set the current room as a favorite./n/nPress %s at any time to go to that room./n/nF7, F8, F9, F10, F11, and F12 can all be favorite rooms.",theButton,theButton);
  else changeRoom2(favRoom[which]);     // changeRoom2 checks for >0 etc.
}
function useOrSetFavoriteRoom(n)
{	if(n <1)return false; if(n >6)return false;
  getRoomName(thisRoom); // updates Global "Xroom name"
  var key ="F7";
  if(n ==1){favorite1.Text =roomName;key ="F7";}
  if(n ==2){favorite2.Text =roomName;key ="F8";}
  if(n ==3){favorite3.Text =roomName;key ="F9";}
  if(n ==4){favorite4.Text =roomName;key ="F10";}
  if(n ==5){favorite5.Text =roomName;key ="F11";}
  if(n ==6){favorite6.Text =roomName;key ="F12";}
  //-------------------------------------------
  if(mouse.IsButtonDown(eMouseLeft)|| mouse.IsButtonDown(eMouseRight)) 
  { favRoom[n] =thisRoom;	favRoomName[n] =roomName;
    Display("'%s' is now a favorite location/n(press %s to go there)",roomName, key);
    return true;
  } // still here? Mouse button is not down then
  pressFavorite(n,key);
}
function setFavoriteMusic(n)
{	if(n <0)return; if(n >9) return;
	favMusic[n] =GetCurrentMusic(); if(gCredits_gui.Visible)preMenuMusic =GetCurrentMusic();
	Display("The current music can be heard at any time by pressing '%d'",n);
}


*/
//[cf]
//[of]:save, open
//[of]:code ideas
//[c]  Chrome: use 'localStorage' instead



/*
// detect browser ------------------------------------------------------------------------------------------------------------------------------------------------------------
navigator.appCodeName 			//  e.g. Mozilla. There is no master list! Just try in different browsers and see what they return.
navigator.appName 	 		//  e.g. Netscape
navigator.appVersion  			//  e.g. 5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1
navigator.cookieEnabled  		//  e.g. true
navigator.platform  			//  e.g. Win32
navigator.userAgent  			//  e.g. Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1
// cookies (saving games!) ----------------------------------------------------------------------------------------------------------------------------------------------
function setCookie(c_name,value,exdays)			// saves these variables. File size: up to 4Kb
{	// code to give values to the variables	
	var myVar=escape(value);				// encodes
	document.cookie=c_name + "=" + c_value; 		// writes the text file
}									// can auto-load default game when yu open the browser! Neat!
fufunction Get_Cookie( variableIwant ) 			// reads every variable in the cookie and returns the value you want, or 'null' if it is not found.
{	var myCookies = document.cookie.split( ';' );	// splits cookie wherever it finds a ";" Javascript automatically expands var to array + adds slots
	var myCookieLine = '';					// ready for separate "name=value" strings
	var var_name = ''; var var_value = '';	// variable for each part of the array
	var cookie_works = false; 				// just in case it doesn't have what I expect
	for ( i = 0; i < myCookies.length; i++ )		// look at each array slot
	{	myCookieLine = myCookies[i].split( '=' ); // split each string wherever it finds a "=": returns an array.
		var_name = myCookieLine[0].replace(/^\s+|\s+$/g, '');// assigns first part of array as var_name, and gets rid of ant white spaces
		if ( var_name ==variableIwant )
		{	cookie_works= true;
			if ( myCookieLine.length > 1 )	// anything there?
				var_value =unescape(myCookieLine[1].replace(/^\s+|\s+$/g, ''));//'unescape' decodes it. Returns null if nothing there.
			return var_value;
			break;					// escapes loop
		}myCookieLine =null;				// still here? Wasn't what you wanted then. Will not crash if old savegame used.
		var_name = '';
	}if (!cookie_works ) return null;			// looped through every variable and still didn't find the one you wanted
}
// 4k limit: use Plotpoint code to reduce savegame to minimum variables.  (Sensible, as every GetCookie reads the whole thing for just one variable.)Then different versions // 
// e.g. 1000 bytes on game name + save name, 2000 on globals (as ints), 1000 on room positions (objects as ints)

*/
//[cf]


function showSaveOpen()
{	menuBack("savem and open");

//[c]  drop down list: use cookies or localStorage?
	// use can override default (= localStorage for Chrome, cookies for everyone else)

//[c]  drop down list: saved cookies (or for Chrome, localStorage)

//[c]  drop down list: cookie slots to save

//[c]  drop down list: go to chapter
	// for when cookies don't work. 


//[c]  SIMPLE TEXT  under drop down lists: explains simple list
	// about cookies, and if want to cheat, "tools" menu has a link to names and triggers

//	fillSpan("SPGUIcontent", bigString);
}

//[cf]
//[of]:tools


function showTools()
{	menuBack("tools");

//[c]  drop down list: text size

//[c]  drop down list: speech speed


//[c]  drop down list: chapter names: SPOILERS!

//[c]  drop down list: triggers: SPOILERS!


//[c]  TEXT BOX: explains options, and emergency help (like a book, you can jump ahead if you want - it's your game!)


//	fillSpan("SPGUIcontent", bigString);
}


/*

font size

music on/off


function changeTextDelay( by)
{	game.text_speed = game.text_speed +by;
	if(game.text_speed >=70)	{ game.text_speed =70; }
	if(game.text_speed <=1)	{ game.text_speed =1; }
	if(by <0) { Display("slower text"); }
	else   { Display("faster text"); }
}

*/

//[cf]
//[of]:copyright

function showCopyright(text)
{	menuBack("copyright and credits",1.6);
	if((text ==null)||(text ==""))
	text ="Image string plus music string: now includes hyperlinks!";
	
//[c]  text link: maximise licence.txt

//[c]  text link: maximise scrolling credits
	// same code as used for final credits (but then the aer is seli transparent)

//[c]  TEXT BOX: current image and music - include live links!

	var textPlus = simpleText(text,"",1,"", -30, 590,200,50,50,2900002); // on top of iframe, in case font is big
//[c]  							simpleText(text, link, font,color,fontSizeAdd,width,height,x,y,z) 
	var div2 	= makeDivString("credits",50,250,2900002);
	var iframe 	= "<iframe width=\"585px\" height=\"220px\" name=\"credits\" src=\"scripts/credits.html\" frameborder=0 ALLOWTRANSPARENCY=\"true\"></iframe>";
	var bigString = textPlus + div2 + iframe +"</div>";
	fillSpan("SPGUIcontent", bigString);
	
}

//[cf]
//[of]:book

function showBook()
{	menuBack("The Picture of Dorian Gray",1.6);

	var div = makeDivString("book",50,50,2900001);
	var bigString = "<iframe width=\"585px\" height=\"420px\" name=\"book\" src=\"scripts/book/book.html\" frameborder=0 ALLOWTRANSPARENCY=\"true\"></iframe>";
	bigString = div +bigString +"</div>";
	fillSpan("SPGUIcontent", bigString);
}



//[cf]
//[c]
//[of]:other
//[of]:floating text

//[c]  text message on screen


/*

function fadeMessage( what, fade, stay, font) // =(text, color, color, delay, delay)
{	var incut =cut;if(cut){cut=0;EndCutscene();} // *************** skip to here
  wait2(3);   // bug fix: otherwise pressing ESC easily skips the next section as well
  if(fade <1) fade =30; // default: 1 second
	if(stay <1) stay =(what.Length *20) +60; // e.g. "1234567890" takes 5 seconds
	fadeLabel.Font =font;
	fadeLabel.Text =what;
  if(overrideSpeechColor)fadeLabel.TextColor =overrideSpeechColor;
  else {resetRoomColors();fadeLabel.TextColor =speechColor;  }  
  gFadetext.Transparency =100;
  gFadetext.Visible =1
	var pause =fade/100;
	if(pause <1) pause =1;
	var step = 100/fade;													// how much to fade,each frame
	if(step <1) step =1;
	var trans =100;
  cut=1;StartCutscene(eSkipAnyKeyOrMouseClick); // *************** allow ESC to skip
	while((trans >0)&&(!game.skipping_cutscene))	// fade up to 100 OR when user skips
	{	gFadetext.Transparency =trans;
		trans = trans - step;
    if(trans <0) trans =0;
		wait2(pause);	
  }cut=0;EndCutscene();// ********************************** jump here if you click again
  gFadetext.Transparency =0;
  wait2(3);   // bug fix: otherwise pressing ESC easily skips the next section as well
  step = step *2; // next: fade down more quickly
  cut=1;StartCutscene(eSkipAnyKeyOrMouseClick); // *************** skipping
  wait2(3);   // bug fix: otherwise pressing ESC easily skips the next section as well
  while(trans <100)																// fade down
	{ gFadetext.Transparency =trans;
		trans =trans +step;
    if(trans >100) trans =100;
		wait2(pause);	}
  gFadetext.Visible =0
  if(!incut){cut=0;EndCutscene();}// ***************************
  wait2(3);   // bug fix: otherwise pressing ESC easily skips the next section as well
}

*/
//[cf]
//[of]:letter


/*
function showLetter( text1,  var text2)
{ var incut =cut;if(incut)endCut(); // ***********************************************************
	let1.Text =text1;
	let2.Text =text2;
  // choose font
  var font =5; // small handwriting
  if((let1.Text.Length <200)&&(let2.Text.Length <200))font =8;  // few words? larger font
  let1.Font =font; let2.Font =font;
	gLetter_gui.Transparency =100;
	gLetter_gui.Visible =1
	var tr =100;	while(tr>0)
	{	tr = tr -8; if(tr <0) tr =0; gLetter_gui.Transparency =tr; wait2(1);} 
	cut=1;StartCutscene(eSkipAnyKeyOrMouseClick); // **********************************
	Wait(1800);// 60 seconds should be plenty of time
	endCut(); // ********************************************************
	wait2(1);
	tr =0;	while(tr<100)
	{	tr = tr +10; if(tr >100) tr =100; gLetter_gui.Transparency =tr; wait2(1);} 
	gLetter_gui.Visible =0
	if(incut)startCut(); // **********************************************
}
*/
//[cf]

//[c]  books in the library

//[c]  flashback overlay



//[cf]

	

//[cf]
//[of]:music\: TO DO
//[c]  copyrights - in prepare_objects etc.

//[of]:music list
/*
function getMusicDetails()
{ var m = GetCurrentMusic();
	mWhat =""; 	mWho =""; // flags as 'not found'
	//--------------------------------------------------------------------------------------------------
	if(m <100)
	{	if(m==1) {mWho= "incomp";mWhat="'Exciting Trailer'"; return;} 														// 1 intro action
		if(m==2) {mWho= "RFCM";  mWhat="from the 'Sanctus' in Faure's Requiem"; return;} 					// 2 intro choir
		if((m==3)||(m ==81)){mWho= "RFCM";  mWhat="adapted from the 'Miserere Mei' by Allegri"; return;} // 3 start-freedom
		if(m==4) {mWho= "Paco";  mWhat="'Miguel'"; return;} 																			// 4 freedom-rebirth
		if(m==5) {mWho= "Paco";  mWhat="'Vals de la nostalgia'"; return;} 												// 5 rebirth-bombshell
		if(m==6) {mWho= "incomp";mWhat="'To The Ends'"; return;} 																	// 6 bombshell-24601
		if(m==7) {mWho= "Paco";  mWhat="'Vals triste'"; return;} 																	// 7 24601-escape
		if(m==8) {mWho= "Paco";  mWhat="'Vagabundo'"; return;} 																		// 8 escape-convent (buy Cosette)
		if(m==9) {mWho= "Paco";  mWhat="'Obsesion'"; return;} 																		// 9 convent-barricades
		if(m==10){mWho= "Paco";  mWhat="'Desesperacion'"; return;}																// 10 barricades-house
		if(m==11){mWho= "RFCM";  mWhat="the 'Humming Chorus' from Puccini's Madame Butterfly'"; return;} // 11 house-suicide (humming)
		if(m==12){mWho= "Paco";  mWhat="'Desde mi ventana'"; return;} 														// 12 suicide-leaves
		if(m==13){mWho= "Paco";  mWhat="'The Sad Man'"; return;} 																	// 13 leaves-end
		if(m==14){mWho= "ChrisWorthProductions.com"; mWhat="'Solveig's Song' by Grieg (Moscow International Symphony Orchestra)"; return;} // 14 after end (Solveig)
		if(m==15){mWho= "Matt";  mWhat="'Les Miserables'"; return;}        												// 15 - MENU -
		if(m==16){mWho= "incomp";mWhat="'Earth Prelude'"; return;}        												// 16 - neutral -
		if((m==17)||(m==80)){mWho= "RFCM";  mWhat="adapted from the 'Introit and Kyrie' from Faure's Requiem"; return;} //  17 sewers
		if(m==18){mWho= "incomp";mWhat="'Trio for Piano Violin and Viola'"; return;} 							// 18 Gillenormand
		if(m==19){mWho= "incomp";mWhat="'Grave Blow'"; return;} 																	// 19 Jondrettes
		if(m==20){mWho= "'likantropika'"; mWhat="'Random gods' from the album 'Came dark and silent'"; return;} // 20 galley
		if(m==21){mWho= "zero";  mWhat="from part 3 of the 'Disabled Emotions' suite"; return;} 	// 21 dock+ship
		if(m==22){mWho= "MforM"; mWhat="flute tracks 2 and 5 from 'Music for Multimedia'"; return;} // forest flute                                   //  22 forest
		if(m==23){mWho= "Paco";  mWhat="'Sonatina'"; return;} 																		// 23 tavern
		if(m==24){mWho= "incomp";mWhat="'Dark Dance'"; return;} 																	// 24 dark backstreets
		if(m==25){mWho= "incomp";mWhat="'Brandenburg No 4, part 1'"; return;} 										// 25 assembly normal
		if(m==26){mWho= "incomp";mWhat="'The Rule'"; return;} 																		// 26 assembly Napoleon
		if(m==27){mWho= "Paco";  mWhat="'Lamento'"; return;} 																			// 27 library and archives
		if(m==28){mWho= "RFCM";  mWhat="adapted from Buckner's 'Ave Maria'"; return;} 						// 28 convent
		if(m==29){mWho= "incomp";mWhat="'Action'"; return;} 																			// 29 ABC
		if(m==30){mWho= "RFCM";  mWhat="adapted from the 'Miserere Mei' by Allegri"; return;} 		// 30 church
		if(m==31)m=111; // 31 heaven
		if(m==32){mWho= "incomp";mWhat="'Parisian'"; return;} 																		// 32 Paris center
		if(m==33){mWho= "mutopia";mWhat="'Ich Grolle Nicht'"; return;} 														// 33 factory
		if(m==34){mWho= "Taylor";mWhat="'Mary'"; return;} 																				// 34 garden
		if(m==35){mWho= "incomp";mWhat="'Crisis'"; return;} 																			// 35 lift cart (1)
		if(m==36){mWho= "RFCM";  mWhat="'Comfort Ye' from Handel's 'Messiah'"; return;} 					// 36 bishop
		if(m==37){mWho= "incomp";mWhat="'We Got Trouble"; return;} 																// 37 final blackmail
		if(m==38){mWho= "incomp";mWhat="from the end of 'Crisis'"; return;} 											// 38 lift cart (2)
		if(m==39){mWho= "blom";  mWhat="'Tsunami'";return;}																				// 39 fire
		if(m==40){mWho= "Ross";  mWhat="'Drone'"; return;} 																				// 40 Virgil in turmoil
		if(m==41){mWho= "RFCM";  mWhat="adapted from Buckner's 'Ave Maria'"; return;}   					// 41 Virgil reborn
		if(m==42){mWho= "Taylor";mWhat="'Asunder'"; return;} 																			// 42 Fantine in police station
		if(m==43){mWho= "Matt";  mWhat="'Les Miserables 2'"; return;}   													// 43 save Fantine from Javert
		if(m==44){mWho= "incomp";mWhat="adapted from 'Achilles' and 'Faceoff'"; return;}  				// 44 in court
		if(m==45){mWho= "Piotr"; mWhat="part of 'Introit' from 'Desiderium'";} 										// 45 court climax
		if(m==46){mWho= "MforM"; mWhat="organ music"; return;} // DIVINE COMEDY
		if(m==47){mWho= "RFCM";  mWhat="adapted from Buckner's 'Ave Maria'"; return;}  						// 47 Fantine dying
		if(m==48){mWho= "Piotr"; mWhat="part of 'Domine' from 'Desiderium'"; return;}   					// 48 Fantine dies
		if(m==49){mWho= "incomp";mWhat="'Plannings'"; return;} 																		// 49 night escape
		if(m==50){mWho= "incomp";mWhat="'Plannings'"; return;} 																		// 50 wall climb
		if(m==51){mWho= "MforM"; mWhat="Piano track 4, from 'Music for Multimedia'"; return;} 		// 51 Gillenormand confrontation
		if((m==52)||(m ==142)){mWho= "Piotr"; mWhat="'Kyrie' from 'Desiderium'"; return;} 				// 52 Marius, 143 = big ending
		if(m==53){mWho= "JMTR";  mWhat="'Wondrous Love (string quartet)'"; return;} 							// 53 Eponine (tragic)
		if((m==54)||(m==78)){mWho= "RFCM";  mWhat="'Qui Tollis' from Mozart's 'Great Mass'"; return;}// 54 barricades+close; 78 = loud death blast
		if(m==55){mWho= "incomp";mWhat="'Fantastic (B1-128)'"; return;} 													// 55 rigging start
		if(m==56){mWho= "blom";  mWhat="'Tsunami'"; return;} 																			// 56 rigging end
		if(m==57){mWho= "MforM"; mWhat="flute track 10, from 'Music for Multimedia'"; return;} 		// 57 find Cosette
		if(m==58){mWho= "incomp";mWhat="'Faceoff'"; return;} 																			// 58 in grave
		if(m==59){mWho= "zero";  mWhat="from part 2 of the 'Disabled Emotions' suite"; return;}  	// 59 escape grave
		if(m==60){mWho= "Paco";  mWhat="'Alicia'"; return;}  																			// 60 fall in love (x2)
		if(m==61){mWho= "MforM"; mWhat="Piano track 21, from 'Music for Multimedia'"; return;} 		// 61 Gillenormand discord
		if(m==62){mWho= "mutopia";mWhat="the French suite 5, 'allemande'"; return;} 							// 62 happy reconciliation
		if(m==63){mWho= "Matt";  mWhat="'Les Miserables 4'"; return;} 														// 63 suicide build up
		if(m==64){mWho= "Matt";  mWhat="'Les Miserables 2'"; return;} 														// 64 suicide
		if(m==65){mWho= "RFCM";  mWhat="from 'Qui Tollis' from Mozart's 'Great Mass'"; return;} 	// 65 realization
		// 66 silence
		if(m==67){mWho= "incomp";mWhat="Land of the Dead"; return;} // DIVINE COMEDY
		if(m==68){mWho= "JMTR";  mWhat="from 'Prayer Drone'"; return;} 														// 68 Fantine is dead
		if(m==69){mWho= "Piotr"; mWhat="part of 'Domine' from 'Desiderium'"; return;} 						// 69 dying Fantine sees Javert
		if(m==70){mWho= "Piotr"; mWhat="part of 'Kyrie' from 'Desiderium'"; return;} 							// 70 Marius realizes father is good
		if(m==71){mWho= "'Bosa'";mWhat="Short tension'"; return;} // ******* UNUSED *******				// 71 (unused: short tension)
		if((m==72)||(m==77)){mWho= "Skidmore";mWhat="from Tchaikovsky's Romeo and Juliet"; return;} // 72 the kiss; 77 Marius with the gunpowder
		if((m==73)||(m==74)||(m==172)){mWho= "Musopen";mWhat="from Dvorak's Mass in D, performed by the Davis High School Symphony Orchestra"; return;} // Eponine in front of house; climax to ATOTC
		if((m==75)||(m==76)||(m==79)||(m==178)){mWho= "Skidmore";mWhat="from Tchaikovsky's 1812 Overture"; return;} // 75,76 death on the barricades (build up and fall); 79 escape to sewers
		//(m==77) see (m==72)
		//(m==78) see (m==54)
		//(m==79) see (m==75)
		//(m==80) see (m==17)
		//(m==81) see (m==3)
		if(m==82){mWho= "Skidmore";mWhat="from 'Night on Bald Mountain' by Rimsky-Korsakov"; return;} // 82: Cosette cold
		//----------------------- Divine Comedy --------------------------- (see also 46 and 67) -----------------
		if(m==83){mWho= "Ham";mWhat="Dies Irae"; return;} //
		if(m==84){mWho= "Ham";mWhat="Shadows of Faith - A Dark Alleluia"; return;} //
		if(m==85)m=114; // gnomus
		if(m==86){mWho= "Skidmore";mWhat="'Night on Bald Mountain'"; return;} //
		if(m==87){mWho= "incomp";mWhat="The Dread"; return;} //  NOTE: 142 is similar, but more intense
		if(m==88){mWho= "incomp";mWhat="adapted from 'Grave Blow'"; return;} //
		if(m==89){mWho= "Ham";mWhat="'Shadows of Fire'"; return;} //
		if(m==90){mWho= "incomp";mWhat="Grave Matters"; return;} //
		if(m==91)m=132; // Mars
		if(m==92){mWho= "incomp";mWhat="adapted from 'Halls of the Undead'"; return;} //
		if(m==93)m=132; // Mars
		if(m==94){mWho= "Olaf";mWhat="'Libera Me' from Verdi's Requiem"; return;} //
		if(m==95){mWho= "YOPO";mWhat="'Heaven' by Jonathan Slatter"; return;} //
		if(m==96){mWho= "Olaf";mWhat="'In Paradisium' from Verdi's Requiem"; return;}
		if(m==97){mWho= "Iain Morland";mWhat="Citadel Ascent"; return;} //
		if(m==98){mWho= "incomp";mWhat="Noire"; return;} //
		if(m==99){mWho= "milne";mWhat="Haunting"; return;} //
	}	if(m >99)// do not use 'else' as some early numbers redirect to later numbers
	{	if(m==100){mWho= "incomp";mWhat="Numinous Shine"; return;} //
		if(m==101){mWho= "RFCM";mWhat="'Morning' from Peer Gynt by Grieg"; return;} //
		if(m==102){mWho= "Ham";mWhat="the end of 'Rum Alleluia'"; return;} //
		if(m==103){mWho= "Ham";mWhat="Lacrimosa"; return;} //
		if(m==104){mWho= "Olaf";mWhat="'Agnus Dei' from Verdi's Requiem"; return;} //
		if(m==105){mWho= "MforM";mWhat="music by Delius"; return;} //
		if(m==106){mWho= "incomp";mWhat="Frozen Star"; return;} //
		if(m==107){mWho= "YOPO";mWhat="'The Friars' Chant' by Jonathan Slatter"; return;} //
		if(m==108){mWho= "Olaf";mWhat="'Te Deum' from Verdi's Requiem"; return;} //
		if(m==109){mWho= "Ham";mWhat="Decorus Angelus"; return;} //
		if(m==110){mWho= "incomp";mWhat="Arcadia"; return;} //
		if(m==111){mWho= "RFCM";  mWhat="adapted from the 'Sanctus' in Faure's Requiem"; return;} // see also 31
		if((m==112)||(m==117)){mWho= "Ham";mWhat="Rum Alleluia"; return;} //
		if(m==113)m=132;
		if(m==114){mWho= "Skidmore";mWhat="'Pictures at an Exhibition, 1, Gnomus'"; return;} //
		if(m==115){mWho= "incomp";mWhat="Schmetterling"; return;} //
		if(m==116){mWho= "Olaf";mWhat="'Libera Me' and 'Te Deum' from Verdi's Requiem"; return;} //
		// for 117 see 112
		//----------------------- Genesis of the Gods ------------------------------------
		if((m==118)||(m==119)||(m==120)||(m==122)||(m==154)){mWho= "incomp";mWhat="Evil Intro"; return;} //
		// for 121 see 129
		if(m==123){mWho= "incomp";mWhat="based on the famous 'divertissement'"; return;} // angel's world
		if(m==124){mWho= "incomp";mWhat="Private Reflection"; return;} // Tartarus
		if(m==125){mWho= "incomp";mWhat="Unrelenting"; return;} // later general
		// 126: was unused. see A Tale of Two Cities
		if(m==127){mWho= "incomp";mWhat="Vortex"; return;} // big bad event
		if(m==128){mWho= "Ham";mWhat="Sun_God"; return;} // after first battle: when Prometheus does his stuff
		if((m==129)||(m ==121)){mWho= "Ham";mWhat="A_Brother's_Death"; return;} // final zoom
		if(m==130){mWho= "Ham";mWhat="The Fall"; return;} // general, excellent as usual
		if(m==131){mWho= "blom";mWhat="Leaving in a Hurry"; return;} // heavy situation
		if(m==132){mWho= "RFCM";mWhat="Mars, from The Planets by Holst"; return;} // various : Dis, and gods battling.
		if(m==133){mWho= "incomp";mWhat="Darkness Is Coming"; return;} // default music, early in the game
		if(m==134){mWho= "Flick3r";mWhat="Civil Destruction"; return;} // final climax, before the zoom
		if(m==135){mWho= "Skidmore";mWhat="'Rosamunde'"; return;} // happy ending
		if(m==136){mWho= "Ham";mWhat="The Wanderer"; return;} // short version of pirates music
		if(m==137){mWho= "MforM";mWhat="Rameau"; return;} // my favorite!
		if(m==138){mWho= "MforM";mWhat="'FNTSML01'"; return;} // eternal alien music
		if(m==139){mWho= "MforM";mWhat="'FNTSML02'"; return;} // other dimension music
		if(m==140){mWho= "MforM";mWhat="'FNTSML48'"; return;} // ticking clock: time travel music
		if(m==141){mWho= "MforM";mWhat="based on 'Pulse'"; return;} // start of falling to Tartarus
		if(m==142){mWho= "incomp";mWhat="Inner Sanctum"; return;} // Tartarus: NOTE: 87 is similar, but less intense
		// for 143 see 52
		//----------------------- A Tale of Two Cities ------------------------------------
    if(m==126){mWho= "Shaw";mWhat="'So Dramatic'"; return;} // generic dramatic violins
		if(((m >143)&&(m <151))||(m==159)){mWho= "Skidmore";mWhat="from Beethoven's Fifth symphony"; return;} // intro to ATOTC
    //144-8 =Beethoven intro,149 = big event, 150 = padding for big event
    if(m==151){mWho= "Skidmore";mWhat="from Beethoven's Sixth symphony"; return;} // dramatic court decision
    if(m==152){mWho= "Shaw";mWhat="'The Angels Weep'"; return;} // final 'aaah' crescendo (first half has multiple uses)
    if(m==153){mWho= "incomp";mWhat="'Agnus Dei'"; return;} // dramatic voices
    if(m==172){mWho= "Musopen";mWhat="from Dvorak's Mass in D, performed by the Davis High School Symphony Orchestra"; return;} // Eponine in front of house; climax to ATOTC
		// see also 73+74: copied twice in case you use 'if(m <80)'
		// 154 = 'Evil Intro' - see 118
    if(m==156){mWho= "Dommusic";mWhat="the Agnus Dei from Haydn's 'Missa in tempore belli'"; return;} // dramatic voices
    if(m==157){mWho= "Dommusic";mWhat="the Benedictus from Haydn's Jubilee Mass"; return;} // Manette
    if(m==158){mWho= "Dommusic";mWhat="the Gloria from Haydn's Jubilee Mass"; return;} // happy, wedding
    // for 159 see 144-150
    if(m==160){mWho= "incomp";mWhat="'A Singular Perversion'"; return;} // default music, early in the game
    if(m==161){mWho= "Skidmore";mWhat="Mussorgsky's 'Pictures at an Exhibition,' La grande porte de Kiev"; return;} // default court
    if((m==162)||(m==163)){mWho= "Skidmore";mWhat="Dvorak's Cello Concerto, Opus 104"; return;} // long start music; darker segment for chateau
		if(m==164){mWho= "incomp";mWhat="'Intended Force'"; return;} // default music, early in the game
		if(m==165){mWho= "incomp";mWhat="'Nerves'"; return;} // generic serious, stress, action
		if(m==166){mWho= "incomp";mWhat="'Devastation and Revenge'"; return;} // generic serious, stress, action
		if(m==167){mWho= "Shaw";mWhat="'House of Evil'"; return;} // Chateaus (literally the 'house of evil'
		if((m==168)||(m==169)){mWho= "incomp";mWhat="'Dark Star'"; return;} // Lucie discovers that Darney is alive
		if((m==155)||(m==170)||(m==171)){mWho= "RFCM";mWhat="Bruch's violin concerto, 2nd movement"; return;} // Lucie discovers that Darney is alive
		// for 172 see 73+74 (Dvorak's Mass): 154 =loop on its own, 155 =loop for climax, 172 = climax
		if(m==173){mWho= "incomp";mWhat="'Clenched Teeth'"; return;} // short shrieking strings
		//----------------------- Monte Cristo ------------------------------------
	if(m==174){mWho= "Ham";mWhat="Sokarahitam"; return;} // lone despair music, excellent as usual
	if((m==175)||(m==176)){mWho= "RFCM";mWhat="'Fingel's Cave' from the Hebrides overture by Mendelssohn"; return;} // lone despair music, excellent as usual
	if(m==177){mWho= "incomp";mWhat="based on 'Mistake the Getaway'"; return;} // generic serious, stress, action
	if(m==178){mWho= "Skidmore";mWhat="from Tchaikovsky's 1812 Overture"; return;} // 75,76 death on the barricades (build up and fall); 79 escape to sewers
	if(m==179){mWho= "incomp";mWhat="based on 'Failing Defense'"; return;} // generic serious, stress, action
	if(m==180){mWho= "incomp";mWhat="based on a version of 'Danse Macabre'"; return;} // generic serious, stress, action
	if(m==181){mWho= "incomp";mWhat="'Dangerous'"; return;} // generic serious, stress, action
	if(m==182){mWho= "incomp";mWhat="based on 'Clenched Teeth'"; return;} //
	if((m==183)||(m==184)||(m==188)||(m==189)){mWho= "RFCM";mWhat="'Saturn' from Holst's Planet Suite"; return;} //
	if((m==185)||(m==186)||(m==187)){mWho= "incomp";mWhat="'Cortosis'"; return;} // 
  }
}

function updateMusicWho()	//--------------------------------------------------------------------------------------------------
{	if(mWho =="blom")  {mWho ="Fredrik Blom (www.fredrikblom.com/music)"; return; }
  if(mWho =="Dommusic"){mWho ="'Dommusic,' the Cathedral Choir of St. Martin, Eisenstadt"; return; }
  if(mWho =="Ham")	 {mWho ="Hamilton Cleverdon (www.garageband.com/ artist/hamilton)"; return; }
	if(mWho =="incomp"){mWho ="Kevin MacLeod (incompetech.com)"; return; }
	if(mWho =="JMTR")  {mWho ="Jonathon Roberts (jmtr.com)"; return; }
	if(mWho =="Matt")  {mWho ="Matthew Hartman"; return; }
	if(mWho =="MforM") {mWho ="Romeo Music International (all rights reserved)"; return; }
	if(mWho =="milne") {mWho ="Matthew Milne (from the Public Music Project)"; return; }
	if(mWho =="Musopen"){mWho="the Musopen online music library (www.musopen.com)"; return; }
	if(mWho =="mutopia"){mWho="the Mutopia Project (www.mutopiaproject.org)"; return; }
	if(mWho =="Olaf")	 {mWho="the St Olaf chapel choir (www.stolaf.edu/depts/music/chapel/)"; return; }
	if(mWho =="Paco")  {mWho ="Paco Santiago, interpreted by Paco and Miguel Santiago (www.pacosantiago.com), dedicated to the memory of Miguel Santiago who passed away in 2001"; return; }
	if(mWho =="Piotr") {mWho ="Piotr Pawlowski"; return; }
	if(mWho =="RFCM")  {mWho ="Dr.Keith Salmon (royalty-free-/nclassical-music.org)"; return; }
	if(mWho =="Ross")  {mWho ="Peter John Ross (sonnyboo.com)"; return; }
	if(mWho =="Shaw")	 {mWho ="Jason Shaw (www.audionautix.com)"; return; }
	if(mWho =="Skidmore"){mWho="the Musopen online music library (www.musopen.com), performed by the Skidmore College Orchestra"; return; }
	if(mWho =="Taylor"){mWho ="Taylor Hayward (www.taylorhayward.org)"; return; }
	if(mWho =="YOPO")	 {mWho ="'Sounds Visual/YOPO' (www.yopo.co.uk)"; return; }
	if(mWho =="zero")	 {mWho ="the 'zero-project' (www.zero-project.gr)"; return; }
}
*/
//[cf]


/*

//-------------------------------------------------------------------------------------------------------------
var mWho; var mWhat;
function updateMusicText()
{	var allText ="";
	getMusicDetails(); 	updateMusicWho();
	if(mWhat !=" ") allText =allText.Format("The music now playing is %s",mWhat);
	if(mWho !=" ") allText =allText.Format("%s, copyright %s",allText,mWho);
	music_credit_label.Text = allText;
}
function fullVolume(){SetDigitalMasterVolume(100);SetMusicMasterVolume(100);}
function fadeOutMusic()
{	var n =100;
	while(n >1){n--; SetDigitalMasterVolume (n);wait2(1);} // takes 3 seconds
	StopMusic();
	wait2(1);
	PlayMusic(66); // silent track
	fullVolume();	 // back to normal
}
function setMusic( mNew)
{	if(mNew <1)return;
  if(mNew == GetCurrentMusic())return;                  // Already playing?
  PlayMusic (mNew);
	setMemoryMusic(mNew);		                            	// remember for credits page
	if(timer_fademusic <1) fullVolume(); 									// just in case: immediately set to 'quiet' if necessary
  if(gCredits_gui.Visible)updateMusicText();
}
function setMusicQuietFade( toWhat)
{ fademusic_track =toWhat;
  SetGameOption(OPT_CROSSFADEMUSIC,2);
  timer_fademusic =170;
  setMusic(66); // 6 is neutral, 66 is silent
}
function setMusicInstant( what)
{ var oldFade = GetGameOption (OPT_CROSSFADEMUSIC);	// 0=no, 1=slow, 2=slowish, 3=medium, 4=fast)
	SetGameOption (OPT_CROSSFADEMUSIC,0);
	setMusic(what);
	wait2(1);
	SetGameOption (OPT_CROSSFADEMUSIC,oldFade);
}
function setMusicSlow( what) // WARNING: needs 2+ seconds before calling again, or else the first is ignored and the second comes on INSTANTLY.
{ var oldFade = GetGameOption (OPT_CROSSFADEMUSIC);	// 0=no, 1=slow, 2=slowish, 3=medium, 4=fast)
	SetGameOption (OPT_CROSSFADEMUSIC,1);
	setMusic(what);
	wait2(1);
	SetGameOption (OPT_CROSSFADEMUSIC,oldFade);
}
function setDefaultMusic() // handled centrally, so can be re-set after cutscene or other unexpected interruption
{	if(overrideMusic >0) return;
  if(overrideMusic <0) {setMusic(0-overrideMusic); return;} 
	if(gameOver)					{setMusic(14); return;} 											// Solveig's song
  if(!chapterDone(OPENING_SCENE)){setMusic(136); return;} // in case you hit ecape
  
  // by room ------------------------------------------------------------------------------
	if(thisRoom ==35){setMusic(136);return;}// dramatic island d'If
	if((thisRoom ==36)||(thisRoom ==1))  // dungeon, or hit Escape in room 1
                      {if(eventsDone(REMEMBER_VILLEFORT_NAPOLEONIC,REMEMBER_CADEROUSSE))setMusic(94);// church chanting
                       else { if(chapterDone(REMEMBER_VILLEFORT_NAPOLEONIC))              setMusic(174);
                              else                                                      setMusic(183);}
                       return; }             
	if(thisRoom ==11){setMusic(92); return;}// dark forest
	if((thisRoom ==3)||(thisRoom ==39)||(thisRoom ==33)){setMusic(175); return;}// coast, small island, fishing boat
	if(thisRoom ==38){setMusic(67); return;}// underwater (only accessed in cut scene?)
	if(thisRoom ==46){setMusic(85); return;}// cave with hint of surprises (red cave music)
	if(thisRoom ==43){setMusic(20); return;}// Les Mis galley (same music as in LesMis game)
	if(thisRoom ==12){setMusic(20); return;}// flashback house
	if(thisRoom ==8) {setMusic(24); return;}// dark guest house
  if(thisRoom ==22){ if(betweenEvents(SAVE_ALBERT_FROM_BANDITS1,SAVE_ALBERT_FROM_BANDITS3))
                                                      {setMusic(181);return 1;}
                        if(chapterDone(FIND_CADEROUSSE)){setMusic(27); return;}// sad, worried music
                        setMusic(25); return;}// default: happy Brandenburg music
	if((thisRoom ==44)||(thisRoom ==45)){setMusic(176); return;}// monte Cristo

	// generic, by time period -----------------------------------------------------------------------
	if(!chapterDone(TELL_ABBE_STORY)){setMusic(21); return;} // thoughtful
  if(!chapterDone(REMEMBER_VILLEFORT_NAPOLEONIC)){setMusic(10); return;} // another thoughtful
  if(!chapterDone(GASP_FOR_AIR)){setMusic(18); return;} // good violin music
  if(!chapterDone(REFIT_SHIP)){setMusic(13); return;} // gentle, sad - finds out about his family
	if(!chapterDone(DO_NOT_KILL_ALBERT)){setMusic(162); return;} // MORE SERIOUS: ATOTC main theme. determined, no doubts: to first serious setback
	if(!chapterDone(FAKE_DEATH)){setMusic(163); return;} // SAME, darkest part: more serious setback
	if(!chapterDone(DANTES_GAINS_COURAGE)){setMusic(54); return;} // VERY SERIOUS: until he gives up
	setMusic(84); return; // RESOLUTION: fake hallalujah. Determined.


	// default + end = Solveig
}
function removeMenuMusic()
{	var nowMusic =GetCurrentMusic();
	if(testmode)Display("Xremoving menu music: current music =%d, pre music =%d",nowMusic,preMenuMusic);
	if(preMenuMusic != GetCurrentMusic())																	// gotta change it?
	{	if(preMenuMusic >0)
		{ var oldFade = GetGameOption (OPT_CROSSFADEMUSIC);	// how fast is it set to fade?
			SetGameOption (OPT_CROSSFADEMUSIC,0);							// change instantly (just like the menu)
			PlayMusic (preMenuMusic);
			SetGameOption (OPT_CROSSFADEMUSIC,oldFade);
			preMenuMusic =0;
		}	else setDefaultMusic();
	}
}
function setMusicRandom( type) // not the short, giant or specialist tracks.
{


var explainedMusic =new Boolean(0);
function explainMusic()
{	Display("Pressing a number from 0 to 9 plays saved music./n/nPressing a number with the mouse down will save the current music to that number./n/nWhen the story changes music may return to normal.");
	explainedMusic =true;
}
function playFavoriteMusic(n)
{	if(mouse.IsButtonDown(eMouseLeft)|| mouse.IsButtonDown(eMouseRight)) 
	{ setFavoriteMusic(n);	return; }							// ALT-1 etc.: play the music
	if(n >9)n =9; if(n <0)n =0;
	if(favMusic[n] ==0){explainMusic();return;}
	PlayMusic(favMusic[n]);
	if(gCredits_gui.Visible || !explainedMusic)explainMusic();
	preMenuMusic =GetCurrentMusic();
	SetGlobalInt(9,10000); 												// no more checking (reset by back/forth buttons)
	updateMusicText();
}

*/
//[cf]
//[of]:save/load\: TO DO
//[cf]

//[of]:RUN THE GAME
//[of]:every so often
//[of]:spontaneous talk
//[c] sponTalk is defined in main variables

function sponItem()	// spontaneous talk
{	this.say		= "";
	this.from 		= 0;	// say in this chapter or later
	this.to			= 100;	// say in this chapter but NOT later
}
function spon(from,to,what)
{	var newSpon 			= sponItem;
	newSpon.say 			= what;
	if(from)newSpon.from 	= from;
	if(to)	newSpon.to 		= to;
	sponTalk.push(newSpon);
}	
function trySpon()
{	for(var i =0; i < sponTalk.length; i++) 
	{	if(chaptersDone(0, sponTalk[i].from -1))
		{	if(!chapterDone(sponTalk[i].to +1))
			{	say("",sponTalk[i].say);
				sponTalk.splice(i,1);
				sponTimer =0;				// reset
			}
		}
	}
}			


//[cf]

function doEverySoOften()// code that does not have to run every frame
{	loop30 =0;
//[c]  hide mouseover if it doesn't move
	mouseStill++; 
	if(mouseStill >5)	// hide mouseover if still for 5 seconds
	{	document.getElementById("mouseOver").style.top  =-100;	
		document.getElementById("mouseOver").style.left = -100;
	}cleanDownloadQueue();
//[c]  strangers
	for(var i =1; i <= rooms[thisRoom].strangers; i++)
		randomWalk(i);
//[c]  spontaneous talk
	sponTimer++;
	if(sponTimer >10)trySpon();
		
}

//[cf]

//[of]:timers
function checkAllCostumes()								// anyone due a picture update?
{	var n =40; 
	for(n =41; n < ww.length; n++)						// check all main characters
	{	if(ww[n].room ==thisRoom)ww[n].checkCostume();
	}
	if(ww[1].room ==thisRoom)							// if 1 stranger is here, they might all be
		 for(n =0; n < 41; n++)
		 {	if(ww[n].room ==thisRoom)ww[n].checkCostume();
		 }
}
//[c]
function doTimers()
{	checkTimeQueue();
	if(!GUIon)checkAllCostumes();
}


function doIfNotCutscene()
{	loop30++; 
	if(loop30 >=30)doEverySoOften();
}


//[cf]

function beginStory()
{	checkJavascript("");
	changeRoom(0); 									// test room: just adds hotspots to loading room
	setInterval("runGameEngine()",timePerFrame); 	// 33 x 30 =990
	pageLoaded =1;
}
function runGameEngine()// started by setInterval("runGameEngine()",66) in 'beginStory()'
{	if(GUIon)									// allows clicks and not much else
	{	doTimers(); 
		return;
	}	
	if(block <1)doIfNotCutscene();					// gameblocked 0
	if(block <2)doTimers();							// gameblocked 0 or 1: 
	if(block <3)doUntimed();						// gameblocked 0 or 1 or 2
}

function doUntimed()
{	gameTime=gameTime+1;
}

//[cf]

